diff options
Diffstat (limited to 'Timeline/ClientApp/src/app/http/mock/timeline.ts')
-rw-r--r-- | Timeline/ClientApp/src/app/http/mock/timeline.ts | 660 |
1 files changed, 0 insertions, 660 deletions
diff --git a/Timeline/ClientApp/src/app/http/mock/timeline.ts b/Timeline/ClientApp/src/app/http/mock/timeline.ts deleted file mode 100644 index 27addd61..00000000 --- a/Timeline/ClientApp/src/app/http/mock/timeline.ts +++ /dev/null @@ -1,660 +0,0 @@ -import { random, without, range } from "lodash"; - -import { BlobWithEtag, NotModified } from "../common"; -import { - IHttpTimelineClient, - HttpTimelineInfo, - TimelineVisibility, - HttpTimelineListQuery, - HttpTimelineNotExistError, - HttpTimelinePostRequest, - HttpTimelineNameConflictError, - HttpTimelinePatchRequest, - HttpTimelinePostInfo, - HttpTimelinePostContent, - HttpTimelinePostPostRequest, - HttpTimelinePostNotExistError, - HttpTimelineGenericPostInfo, -} from "../timeline"; -import { HttpUser } from "../user"; - -import { mockStorage, sha1, mockPrepare } from "./common"; -import { getUser, MockUserNotExistError, checkToken } from "./user"; - -async function getTimelineNameList(): Promise<string[]> { - return (await mockStorage.getItem<string[]>("timelines")) ?? []; -} - -async function setTimelineNameList(newOne: string[]): Promise<void> { - await mockStorage.setItem<string[]>("timelines", newOne); -} - -type TimelinePropertyKey = - | "uniqueId" - | "lastModified" - | "owner" - | "description" - | "visibility" - | "members" - | "currentPostId"; - -function getTimelinePropertyKey( - name: string, - property: TimelinePropertyKey -): string { - return `timeline.${name}.${property}`; -} - -function getTimelinePropertyValue<T>( - name: string, - property: TimelinePropertyKey -): Promise<T> { - return mockStorage.getItem<T>( - getTimelinePropertyKey(name, property) - ) as Promise<T>; -} - -function setTimelinePropertyValue<T>( - name: string, - property: TimelinePropertyKey, - value: T -): Promise<void> { - return mockStorage - .setItem<T>(getTimelinePropertyKey(name, property), value) - .then(); -} - -function updateTimelineLastModified(name: string): Promise<void> { - return setTimelinePropertyValue( - name, - "lastModified", - new Date().toISOString() - ); -} - -interface HttpTimelineInfoEx extends HttpTimelineInfo { - memberUsernames: string[]; -} - -function createUniqueId(): string { - const s = "abcdefghijklmnopqrstuvwxz0123456789"; - let result = ""; - for (let i = 0; i < 16; i++) { - result += s[random(0, s.length - 1)]; - } - return result; -} - -class MockTimelineNotExistError extends Error { - constructor() { - super("Timeline not exist."); - } -} - -class MockTimelineAlreadyExistError extends Error { - constructor() { - super("Timeline already exist."); - } -} - -async function getTimelineInfo(name: string): Promise<HttpTimelineInfoEx> { - let owner: HttpUser; - if (name.startsWith("@")) { - const ownerUsername = name.substr(1); - owner = await getUser(ownerUsername); - const optionalUniqueId = await getTimelinePropertyValue<string | null>( - name, - "uniqueId" - ); - if (optionalUniqueId == null) { - await setTimelineNameList([...(await getTimelineNameList()), name]); - await setTimelinePropertyValue(name, "uniqueId", createUniqueId()); - await updateTimelineLastModified(name); - } - } else { - const optionalOwnerUsername = await getTimelinePropertyValue<string | null>( - name, - "owner" - ); - if (optionalOwnerUsername == null) { - throw new MockTimelineNotExistError(); - } else { - owner = await getUser(optionalOwnerUsername); - } - } - - const memberUsernames = - (await getTimelinePropertyValue<string[] | null>(name, "members")) ?? []; - const members = await Promise.all( - memberUsernames.map(async (username) => { - return await getUser(username); - }) - ); - - return { - name, - uniqueId: await getTimelinePropertyValue<string>(name, "uniqueId"), - owner, - description: - (await getTimelinePropertyValue<string | null>(name, "description")) ?? - "", - visibility: - (await getTimelinePropertyValue<TimelineVisibility | null>( - name, - "visibility" - )) ?? "Register", - lastModified: new Date( - await getTimelinePropertyValue<string>(name, "lastModified") - ), - members, - memberUsernames, - }; -} - -async function createTimeline(name: string, owner: string): Promise<void> { - const optionalOwnerUsername = await getTimelinePropertyValue<string | null>( - name, - "owner" - ); - if (optionalOwnerUsername != null) { - throw new MockTimelineAlreadyExistError(); - } - - await setTimelineNameList([...(await getTimelineNameList()), name]); - await setTimelinePropertyValue(name, "uniqueId", createUniqueId()); - await setTimelinePropertyValue(name, "owner", owner); - await updateTimelineLastModified(name); -} - -type TimelinePostPropertyKey = - | "type" - | "data" - | "etag" - | "author" - | "time" - | "lastUpdated"; - -function getTimelinePostPropertyKey( - timelineName: string, - id: number, - propertyKey: TimelinePostPropertyKey -): string { - return `timeline.${timelineName}.posts.${id}.${propertyKey}`; -} - -function getTimelinePostPropertyValue<T>( - timelineName: string, - id: number, - propertyKey: TimelinePostPropertyKey -): Promise<T> { - return mockStorage.getItem<T>( - getTimelinePostPropertyKey(timelineName, id, propertyKey) - ) as Promise<T>; -} - -function setTimelinePostPropertyValue<T>( - timelineName: string, - id: number, - propertyKey: TimelinePostPropertyKey, - value: T -): Promise<T> { - return mockStorage.setItem( - getTimelinePostPropertyKey(timelineName, id, propertyKey), - value - ); -} - -function removeTimelinePostProperty( - timelineName: string, - id: number, - propertyKey: TimelinePostPropertyKey -): Promise<void> { - return mockStorage.removeItem( - getTimelinePostPropertyKey(timelineName, id, propertyKey) - ); -} - -async function getTimelinePostInfo( - timelineName: string, - id: number -): Promise<HttpTimelineGenericPostInfo> { - const currentPostId = await getTimelinePropertyValue<number | null>( - timelineName, - "currentPostId" - ); - if (currentPostId == null || id > currentPostId) { - throw new HttpTimelinePostNotExistError(); - } - - const type = await getTimelinePostPropertyValue<string | null>( - timelineName, - id, - "type" - ); - - if (type == null) { - return { - id, - author: await getUser( - await getTimelinePostPropertyValue<string>(timelineName, id, "author") - ), - time: new Date( - await getTimelinePostPropertyValue<string>(timelineName, id, "time") - ), - lastUpdated: new Date( - await getTimelinePostPropertyValue<string>( - timelineName, - id, - "lastUpdated" - ) - ), - deleted: true, - }; - } else { - let content: HttpTimelinePostContent; - if (type === "text") { - content = { - type: "text", - text: await getTimelinePostPropertyValue(timelineName, id, "data"), - }; - } else { - content = { - type: "image", - }; - } - - return { - id, - author: await getUser( - await getTimelinePostPropertyValue<string>(timelineName, id, "author") - ), - time: new Date( - await getTimelinePostPropertyValue<string>(timelineName, id, "time") - ), - lastUpdated: new Date( - await getTimelinePostPropertyValue<string>( - timelineName, - id, - "lastUpdated" - ) - ), - content, - deleted: false, - }; - } -} - -export class MockHttpTimelineClient implements IHttpTimelineClient { - async listTimeline( - query: HttpTimelineListQuery - ): Promise<HttpTimelineInfo[]> { - await mockPrepare("timeline.list"); - return ( - await Promise.all( - (await getTimelineNameList()).map((name) => getTimelineInfo(name)) - ) - ).filter((timeline) => { - if ( - query.visibility != null && - query.visibility !== timeline.visibility - ) { - return false; - } - if (query.relate != null) { - if (query.relateType === "own") { - if (timeline.owner.username !== query.relate) { - return false; - } - } else if (query.relateType === "join") { - if (!timeline.memberUsernames.includes(query.relate)) { - return false; - } - } else if ( - timeline.owner.username !== query.relate && - !timeline.memberUsernames.includes(query.relate) - ) { - return false; - } - } - return true; - }); - } - - getTimeline(timelineName: string): Promise<HttpTimelineInfo>; - getTimeline( - timelineName: string, - query: { - checkUniqueId?: string; - } - ): Promise<HttpTimelineInfo>; - getTimeline( - timelineName: string, - query: { - checkUniqueId?: string; - ifModifiedSince: Date; - } - ): Promise<HttpTimelineInfo | NotModified>; - async getTimeline( - timelineName: string, - query?: { - checkUniqueId?: string; - ifModifiedSince?: Date; - } - ): Promise<HttpTimelineInfo | NotModified> { - await mockPrepare("timeline.get"); - try { - const timeline = await getTimelineInfo(timelineName); - if (query != null && query.ifModifiedSince != null) { - if (timeline.lastModified >= query.ifModifiedSince) { - return timeline; - } else { - if ( - query.checkUniqueId != null && - timeline.uniqueId != query.checkUniqueId - ) { - return timeline; - } else { - return new NotModified(); - } - } - } - - return timeline; - } catch (e) { - if ( - e instanceof MockTimelineNotExistError || - e instanceof MockUserNotExistError - ) { - throw new HttpTimelineNotExistError(); - } - throw e; - } - } - - async postTimeline( - req: HttpTimelinePostRequest, - token: string - ): Promise<HttpTimelineInfo> { - await mockPrepare("timeline.post"); - const user = checkToken(token); - try { - await createTimeline(req.name, user); - } catch (e) { - if (e instanceof MockTimelineAlreadyExistError) { - throw new HttpTimelineNameConflictError(); - } - throw e; - } - return await getTimelineInfo(req.name); - } - - async patchTimeline( - timelineName: string, - req: HttpTimelinePatchRequest, - _token: string - ): Promise<HttpTimelineInfo> { - await mockPrepare("timeline.patch"); - let modified = false; - if (req.description != null) { - modified = true; - await setTimelinePropertyValue( - timelineName, - "description", - req.description - ); - } - if (req.visibility != null) { - modified = true; - await setTimelinePropertyValue( - timelineName, - "visibility", - req.visibility - ); - } - if (modified) { - await updateTimelineLastModified(timelineName); - } - return await getTimelineInfo(timelineName); - } - - async deleteTimeline(timelineName: string, _token: string): Promise<void> { - await mockPrepare("timeline.delete"); - await setTimelineNameList( - without(await getTimelineNameList(), timelineName) - ); - await mockStorage.removeItem( - getTimelinePropertyKey(timelineName, "uniqueId") - ); - - // TODO: remove other things - } - - async memberPut( - timelineName: string, - username: string, - _token: string - ): Promise<void> { - await mockPrepare("timeline.member.put"); - const oldMembers = - (await getTimelinePropertyValue<string[] | null>( - timelineName, - "members" - )) ?? []; - if (!oldMembers.includes(username)) { - await setTimelinePropertyValue(timelineName, "members", [ - ...oldMembers, - username, - ]); - await updateTimelineLastModified(timelineName); - } - } - - async memberDelete( - timelineName: string, - username: string, - _token: string - ): Promise<void> { - await mockPrepare("timeline.member.delete"); - const oldMembers = - (await getTimelinePropertyValue<string[] | null>( - timelineName, - "members" - )) ?? []; - if (oldMembers.includes(username)) { - await setTimelinePropertyValue( - timelineName, - "members", - without(oldMembers, username) - ); - await updateTimelineLastModified(timelineName); - } - } - - listPost( - timelineName: string, - token?: string - ): Promise<HttpTimelinePostInfo[]>; - listPost( - timelineName: string, - token: string | undefined, - query: { - modifiedSince?: Date; - includeDeleted?: false; - } - ): Promise<HttpTimelinePostInfo[]>; - listPost( - timelineName: string, - token: string | undefined, - query: { - modifiedSince?: Date; - includeDeleted: true; - } - ): Promise<HttpTimelineGenericPostInfo[]>; - async listPost( - timelineName: string, - _token?: string, - query?: { - modifiedSince?: Date; - includeDeleted?: boolean; - } - ): Promise<HttpTimelineGenericPostInfo[]> { - await mockPrepare("timeline.post.list"); - // TODO: Permission check. - - const currentPostId = await getTimelinePropertyValue<number | null>( - timelineName, - "currentPostId" - ); - - return ( - await Promise.all( - range(1, currentPostId == null ? 1 : currentPostId + 1).map( - async (id) => { - return await getTimelinePostInfo(timelineName, id); - } - ) - ) - ) - .filter((post) => { - if (query?.includeDeleted !== true && post.deleted) { - return false; - } - return true; - }) - .filter((post) => { - if (query?.modifiedSince != null) { - return post.lastUpdated >= query.modifiedSince; - } - return true; - }); - } - - getPostData( - timelineName: string, - postId: number, - token: string - ): Promise<BlobWithEtag>; - async getPostData( - timelineName: string, - postId: number, - _token?: string, - etag?: string - ): Promise<BlobWithEtag | NotModified> { - await mockPrepare("timeline.post.data.get"); - // TODO: Permission check. - - const optionalSavedEtag = await getTimelinePostPropertyValue<string>( - timelineName, - postId, - "etag" - ); - - if (optionalSavedEtag == null) { - const optionalType = await getTimelinePostPropertyValue<string>( - timelineName, - postId, - "type" - ); - - if (optionalType != null) { - throw new Error("Post of this type has no data."); - } else { - throw new HttpTimelinePostNotExistError(); - } - } - - if (etag === optionalSavedEtag) { - return new NotModified(); - } - - return { - data: await getTimelinePostPropertyValue<Blob>( - timelineName, - postId, - "data" - ), - etag: optionalSavedEtag, - }; - } - - async postPost( - timelineName: string, - req: HttpTimelinePostPostRequest, - token: string - ): Promise<HttpTimelinePostInfo> { - await mockPrepare("timeline.post.post"); - const user = checkToken(token); - - const savedId = await getTimelinePropertyValue<number | null>( - timelineName, - "currentPostId" - ); - const id = savedId ? savedId + 1 : 1; - await setTimelinePropertyValue(timelineName, "currentPostId", id); - - await setTimelinePostPropertyValue(timelineName, id, "author", user); - - const currentTimeString = new Date().toISOString(); - await setTimelinePostPropertyValue( - timelineName, - id, - "lastUpdated", - currentTimeString - ); - - await setTimelinePostPropertyValue( - timelineName, - id, - "time", - req.time != null ? req.time.toISOString() : currentTimeString - ); - - const { content } = req; - if (content.type === "text") { - await setTimelinePostPropertyValue(timelineName, id, "type", "text"); - await setTimelinePostPropertyValue( - timelineName, - id, - "data", - content.text - ); - } else { - await setTimelinePostPropertyValue(timelineName, id, "type", "image"); - await setTimelinePostPropertyValue( - timelineName, - id, - "data", - content.data - ); - await setTimelinePostPropertyValue( - timelineName, - id, - "etag", - await sha1(content.data) - ); - } - - return (await getTimelinePostInfo( - timelineName, - id - )) as HttpTimelinePostInfo; - } - - async deletePost( - timelineName: string, - postId: number, - _token: string - ): Promise<void> { - await mockPrepare("timeline.post.delete"); - // TODO: permission check - await removeTimelinePostProperty(timelineName, postId, "type"); - await removeTimelinePostProperty(timelineName, postId, "data"); - await removeTimelinePostProperty(timelineName, postId, "etag"); - await setTimelinePostPropertyValue( - timelineName, - postId, - "lastUpdated", - new Date().toISOString() - ); - } -} |