Skip to content

Commit

Permalink
feat(types): create /frontend subpackage (#75)
Browse files Browse the repository at this point in the history
feat(types): define `Item` as a discriminated union type
fix: update `extra` prop on `Member` (add `lang` and `favoriteItems`)
build: use typesVersions to point TypeScript to the types of /frontend subpackage
feat(types): move query-client types to /frontend
feat(types): add a magic `ImmutableCast<T>` type to derive `*Record` (immutable) types from backend (mutable) types. Supports nested and optional props with basic or custom types.
fix: use generic type on `getExtra*` methods to infer return type from input type
  • Loading branch information
spaenleh authored Feb 16, 2023
1 parent 80bd60f commit e21d1e5
Show file tree
Hide file tree
Showing 31 changed files with 2,459 additions and 1,105 deletions.
4 changes: 0 additions & 4 deletions .husky/post-checkout

This file was deleted.

2 changes: 1 addition & 1 deletion .prettierrc
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"tabWidth": 2,
"semi": true,
"singleQuote": true,
"importOrder": ["^@?fastify", "^@?graasp", "^[./]"],
"importOrder": ["^@?fastify", "^@?graasp", "^(@/)|[./]"],
"importOrderSeparation": true,
"importOrderSortSpecifiers": true
}
541 changes: 541 additions & 0 deletions .yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions .yarnrc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,8 @@ defaultSemverRangePrefix: ""

nodeLinker: node-modules

plugins:
- path: .yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs
spec: "@yarnpkg/plugin-interactive-tools"

yarnPath: .yarn/releases/yarn-3.3.0.cjs
63 changes: 39 additions & 24 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,55 +16,70 @@
"typescript": {
"definition": "dist/index.d.ts"
},
"exports": {
".": "./dist/index.js",
"./frontend": "./dist/frontend/index.js",
"./package.json": "./package.json"
},
"typesVersions": {
"*": {
"frontend": [
"dist/frontend/index.d.ts"
]
}
},
"files": [
"dist"
],
"dependencies": {
"@fastify/secure-session": "5.2.0",
"aws-sdk": "2.1111.0",
"fastify": "3.29.1",
"@fastify/secure-session": "5.3.0",
"aws-sdk": "2.1310.0",
"fastify": "3.29.5",
"fluent-json-schema": "3.1.0",
"immutable": "4.1.0",
"immutable": "4.2.4",
"js-cookie": "3.0.1",
"qs": "6.11.0",
"slonik": "28.1.1",
"uuid": "8.3.2"
"uuid": "9.0.0"
},
"scripts": {
"pre-commit": "yarn prettier:check && yarn lint",
"hooks:uninstall": "husky uninstall",
"hooks:install": "husky install",
"build": "tsc",
"build": "tsc && tsc-alias",
"type-check": "tsc --noEmit",
"test": "jest --silent",
"test:watch": "yarn test --watchAll",
"lint": "eslint {src,test}",
"prettier:check": "prettier --check src/**/*.ts",
"prettier:write": "prettier --write src/**/*.ts",
"prepack": "yarn build",
"prepare": "yarn prepack && yarn hooks:install"
"prepare": "yarn prepack && yarn hooks:install",
"check": "yarn prettier:check && yarn lint && yarn type-check"
},
"devDependencies": {
"@commitlint/cli": "17.0.3",
"@commitlint/config-conventional": "17.0.3",
"@trivago/prettier-plugin-sort-imports": "3.2.0",
"@types/eslint": "8.2.1",
"@types/jest": "27.4.0",
"@commitlint/cli": "17.4.2",
"@commitlint/config-conventional": "17.4.2",
"@trivago/prettier-plugin-sort-imports": "4.0.0",
"@types/eslint": "8.21.0",
"@types/jest": "27.5.2",
"@types/js-cookie": "3.0.2",
"@types/jsdom": "16.2.14",
"@types/jsdom": "16.2.15",
"@types/lodash.clonedeep": "4.5.7",
"@types/qs": "6.9.7",
"@types/uuid": "8.3.4",
"@typescript-eslint/eslint-plugin": "5.9.0",
"@typescript-eslint/parser": "5.9.0",
"eslint": "8.6.0",
"eslint-config-prettier": "8.3.0",
"husky": "7.0.4",
"jest": "27.4.7",
"@types/uuid": "9.0.0",
"@typescript-eslint/eslint-plugin": "5.51.0",
"@typescript-eslint/parser": "5.51.0",
"eslint": "8.33.0",
"eslint-config-prettier": "8.6.0",
"husky": "8.0.3",
"jest": "27.5.1",
"jsdom": "19.0.0",
"prettier": "2.5.1",
"ts-jest": "27.1.2",
"ts-node": "10.4.0",
"typescript": "4.3.5"
"prettier": "2.8.3",
"ts-jest": "27.1.5",
"ts-node": "10.9.1",
"tsc-alias": "1.8.2",
"typescript": "4.9.5"
},
"packageManager": "[email protected]"
}
1 change: 1 addition & 0 deletions src/constants/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ export * from './constants';
export * from './httpMethod';
export * from './mentions';
export * from './limits';
export * from './itemLogin';
4 changes: 4 additions & 0 deletions src/constants/itemLogin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export enum ItemLoginSchema {
USERNAME = 'username',
USERNAME_AND_PASSWORD = 'username+password',
}
1 change: 1 addition & 0 deletions src/frontend/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './types';
206 changes: 206 additions & 0 deletions src/frontend/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
import type { List, RecordOf } from 'immutable';

import type {
AppItemType,
Category,
CategoryType,
ChatMention,
ChatMessage,
DocumentItemType,
EmbeddedLinkItemType,
Etherpad,
EtherpadItemType,
ExportedChatMessage,
ExportedItemChat,
Flag,
FolderItemType,
H5PItemType,
Invitation,
Item,
ItemCategory,
ItemChat,
ItemLoginSchema,
ItemMembership,
ItemTag,
LocalFileItemType,
Member,
MemberExtra,
MemberMentions,
S3FileItemType,
ShortcutItemType,
} from '@/index';
import type { UUID } from '@/types';

/**
* Convenience type to convert nested objects to deeply immutable objects
*/
export type ImmutableCast<Type> = RecordOf<{
[Property in keyof Type]: Type[Property] extends (infer U)[] | undefined // check that type is an array (or an optional array)
? U extends object // check if internal array type is a custom type
? List<ImmutableCast<U>> // if is custom type transform to List of an immutable transformation of the custom type
: List<U> // else just wrap in a List
: Type[Property] extends object | undefined // check that type is a custom type (or optional custom type)
? ImmutableCast<Type[Property]> // if is custom type then transform to immutable custom type
: Type[Property]; // else (it is a base type) just return the type
}>;

export type AppItemTypeRecord = ImmutableCast<AppItemType>;
export type DocumentItemTypeRecord = ImmutableCast<DocumentItemType>;
export type FolderItemTypeRecord = ImmutableCast<FolderItemType>;
export type H5PItemTypeRecord = ImmutableCast<H5PItemType>;
export type EmbeddedLinkItemTypeRecord = ImmutableCast<EmbeddedLinkItemType>;
export type LocalFileItemTypeRecord = ImmutableCast<LocalFileItemType>;
export type S3FileItemTypeRecord = ImmutableCast<S3FileItemType>;
export type ShortcutItemTypeRecord = ImmutableCast<ShortcutItemType>;
export type EtherpadItemTypeRecord = ImmutableCast<EtherpadItemType>;

export type ItemRecord =
| AppItemTypeRecord
| DocumentItemTypeRecord
| FolderItemTypeRecord
| H5PItemTypeRecord
| EmbeddedLinkItemTypeRecord
| LocalFileItemTypeRecord
| S3FileItemTypeRecord
| ShortcutItemTypeRecord
| EtherpadItemTypeRecord;

export type EtherpadRecord = ImmutableCast<Etherpad>;

export type MemberExtraRecord = ImmutableCast<MemberExtra>;

export type MemberRecord = ImmutableCast<Member<MemberExtra>>;

export type ItemMembershipRecord = ImmutableCast<ItemMembership>;

export type ChatMentionRecord = ImmutableCast<ChatMention>;

export type MemberMentionsRecord = ImmutableCast<MemberMentions>;

/**
* A `CategoryRecord` represents a sort of "tag" for an item. For example: "Math", "Kindergarten" etc ...
*/
export type CategoryRecord = ImmutableCast<Category>;

/**
* A `CategoryTypeRecord` represents a higher order grouping of `CategoryRecord`s like "discipline", "education level" or "language"
*/
export type CategoryTypeRecord = ImmutableCast<CategoryType>;

export type ChatMessageRecord = ImmutableCast<ChatMessage>;

export type ItemChatRecord = ImmutableCast<ItemChat>;

export type ItemTagRecord = ImmutableCast<ItemTag>;

export type FlagRecord = ImmutableCast<Flag>;

export type InvitationRecord = ImmutableCast<Invitation>;

export type ItemCategoryRecord = ImmutableCast<ItemCategory>;

export type ItemLogin = {
loginSchema: ItemLoginSchema;
};

export type ItemLoginRecord = ImmutableCast<ItemLogin>;

export type ExportedChatMessageRecord = ImmutableCast<ExportedChatMessage>;

export type ExportedItemChatRecord = ImmutableCast<ExportedItemChat>;

// a combined record from item-validation, item-validation-review, item-validation-process
export type FullValidation = {
id: string;
itemId: string;
reviewStatusId: string;
validationStatusId: string;
validationResult: string;
process: string;
createdAt: string;
};

export type FullValidationRecord = ImmutableCast<FullValidation>;

export type ItemValidationAndReview = {
itemValidationId: string;
reviewStatusId: string;
reviewReason: string;
createdAt: string;
};

export type ItemValidationAndReviewRecord =
ImmutableCast<ItemValidationAndReview>;

export type ItemValidationGroup = {
id: string;
itemId: string;
itemValidationId: string;
processId: string;
statusId: string;
result: string;
updatedAt: string;
createdAt: string;
};

export type ItemValidationGroupRecord = ImmutableCast<ItemValidationGroup>;

export type Status = {
id: string;
name: string;
};

export type StatusRecord = ImmutableCast<Status>;

export interface Action {
id: string;
itemId: UUID;
memberId: UUID;
}
export type ActionRecord = ImmutableCast<Action>;

export type ActionMetadata = {
numActionsRetrieved: number;
requestedSampleSize: number;
};
export type ActionMetadataRecord = ImmutableCast<ActionMetadata>;

export interface ActionData {
actions: ActionRecord[];
descendants: Item[];
item: Item;
itemMemberships: ItemMembership[];
members: Member[];
metadata: ActionMetadata;
}
export type ActionDataRecord = ImmutableCast<ActionData>;

export type Password = string;
export type NewInvitation = Pick<Invitation, 'email' & 'permission'> &
Partial<Invitation>;

export type ItemLike = {
id: UUID;
itemId: UUID;
memberId: string;
createdAt: string;
};

export type ItemLikeRecord = ImmutableCast<ItemLike>;

// todo: check exact value of extra prop
export type App = {
name: string;
url: string;
description: string;
extra: any;
};

export type AppRecord = ImmutableCast<App>;

export type Tag = {
id: UUID;
name: string;
};

export type TagRecord = ImmutableCast<Tag>;
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ export * from './interfaces';
export * from './services';
export * from './declarations';
export * from './config';
export * from './types';
1 change: 1 addition & 0 deletions src/services/categories/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './types';
30 changes: 30 additions & 0 deletions src/services/categories/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { UUID } from '@/types';

/**
* Higher order categories for `Categories`.
* For example: "discipline", "education level", "language"
*
*/
export type CategoryType = {
id: UUID;
name: string;
};

/**
* `Category` represents a sort of "tag" for items.
* For example you can create a "Math" category which would then relate to the `CategoryType`of "discipline"
* @field type is a foreign key to a `CategoryType` instance
*/
export type Category = {
id: UUID;
name: string;
type: UUID;
};

export type ItemCategory = {
id: UUID;
itemId: UUID;
categoryId: UUID;
createdAt: string;
creator: string;
};
1 change: 1 addition & 0 deletions src/services/chat/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './types';
Loading

0 comments on commit e21d1e5

Please sign in to comment.