From 9014a5b52e9810eb71bc88b3645b0e716a8576ca Mon Sep 17 00:00:00 2001 From: crupest Date: Fri, 31 Jul 2020 23:13:03 +0800 Subject: Add http get timeline api query params. --- Timeline/ClientApp/src/app/http/common.ts | 10 ++++ Timeline/ClientApp/src/app/http/mock/timeline.ts | 62 +++++++++++++++++++- Timeline/ClientApp/src/app/http/timeline.ts | 74 ++++++++++++++++++++++-- Timeline/ClientApp/src/app/utilities/url.ts | 13 ++++- 4 files changed, 150 insertions(+), 9 deletions(-) (limited to 'Timeline/ClientApp/src') diff --git a/Timeline/ClientApp/src/app/http/common.ts b/Timeline/ClientApp/src/app/http/common.ts index 8fb8eb69..3c2f2ba6 100644 --- a/Timeline/ClientApp/src/app/http/common.ts +++ b/Timeline/ClientApp/src/app/http/common.ts @@ -119,6 +119,16 @@ export function convertToNetworkError( } } +export function extractDataOrConvert304ToNotModified( + res: AxiosResponse +): T | NotModified { + if (res.status === 304) { + return new NotModified(); + } else { + return res.data; + } +} + export function convertToBlobWithEtag(res: AxiosResponse): BlobWithEtag { return { data: res.data, diff --git a/Timeline/ClientApp/src/app/http/mock/timeline.ts b/Timeline/ClientApp/src/app/http/mock/timeline.ts index 2a34ef10..911da2f2 100644 --- a/Timeline/ClientApp/src/app/http/mock/timeline.ts +++ b/Timeline/ClientApp/src/app/http/mock/timeline.ts @@ -31,6 +31,7 @@ async function setTimelineNameList(newOne: string[]): Promise { type TimelinePropertyKey = | 'uniqueId' + | 'lastModified' | 'owner' | 'description' | 'visibility' @@ -61,6 +62,14 @@ function setTimelinePropertyValue( .then(); } +function updateTimelineLastModified(name: string): Promise { + return setTimelinePropertyValue( + name, + 'lastModified', + new Date().toISOString() + ); +} + interface HttpTimelineInfoEx extends HttpTimelineInfo { memberUsernames: string[]; } @@ -98,6 +107,7 @@ async function getTimelineInfo(name: string): Promise { if (optionalUniqueId == null) { await setTimelineNameList([...(await getTimelineNameList()), name]); await setTimelinePropertyValue(name, 'uniqueId', createUniqueId()); + await updateTimelineLastModified(name); } } else { const optionalOwnerUsername = await getTimelinePropertyValue( @@ -131,6 +141,9 @@ async function getTimelineInfo(name: string): Promise { name, 'visibility' )) ?? 'Register', + lastModified: new Date( + await getTimelinePropertyValue(name, 'lastModified') + ), members, memberUsernames, }; @@ -148,6 +161,7 @@ async function createTimeline(name: string, owner: string): Promise { await setTimelineNameList([...(await getTimelineNameList()), name]); await setTimelinePropertyValue(name, 'uniqueId', createUniqueId()); await setTimelinePropertyValue(name, 'owner', owner); + await updateTimelineLastModified(name); } type TimelinePostPropertyKey = @@ -304,10 +318,46 @@ export class MockHttpTimelineClient implements IHttpTimelineClient { }); } - async getTimeline(timelineName: string): Promise { + getTimeline(timelineName: string): Promise; + getTimeline( + timelineName: string, + query: { + checkUniqueId?: string; + } + ): Promise; + getTimeline( + timelineName: string, + query: { + checkUniqueId?: string; + ifModifiedSince: Date; + } + ): Promise; + async getTimeline( + timelineName: string, + query?: { + checkUniqueId?: string; + ifModifiedSince?: Date; + } + ): Promise { await mockPrepare(); try { - return await getTimelineInfo(timelineName); + 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 || @@ -342,7 +392,9 @@ export class MockHttpTimelineClient implements IHttpTimelineClient { _token: string ): Promise { await mockPrepare(); + let modified = false; if (req.description != null) { + modified = true; await setTimelinePropertyValue( timelineName, 'description', @@ -350,12 +402,16 @@ export class MockHttpTimelineClient implements IHttpTimelineClient { ); } if (req.visibility != null) { + modified = true; await setTimelinePropertyValue( timelineName, 'visibility', req.visibility ); } + if (modified) { + await updateTimelineLastModified(timelineName); + } return await getTimelineInfo(timelineName); } @@ -387,6 +443,7 @@ export class MockHttpTimelineClient implements IHttpTimelineClient { ...oldMembers, username, ]); + await updateTimelineLastModified(timelineName); } } @@ -407,6 +464,7 @@ export class MockHttpTimelineClient implements IHttpTimelineClient { 'members', without(oldMembers, username) ); + await updateTimelineLastModified(timelineName); } } diff --git a/Timeline/ClientApp/src/app/http/timeline.ts b/Timeline/ClientApp/src/app/http/timeline.ts index 458ea6e6..bfe0d1ad 100644 --- a/Timeline/ClientApp/src/app/http/timeline.ts +++ b/Timeline/ClientApp/src/app/http/timeline.ts @@ -24,6 +24,7 @@ export interface HttpTimelineInfo { description: string; owner: HttpUser; visibility: TimelineVisibility; + lastModified: Date; members: HttpUser[]; } @@ -115,6 +116,16 @@ export class HttpTimelineNameConflictError extends Error { //-------------------- begin: internal model -------------------- +interface RawTimelineInfo { + uniqueId: string; + name: string; + description: string; + owner: HttpUser; + visibility: TimelineVisibility; + lastModified: string; + members: HttpUser[]; +} + interface RawTimelinePostTextContent { type: 'text'; text: string; @@ -171,6 +182,13 @@ interface RawTimelinePostPostRequest { //-------------------- end: internal model -------------------- +function processRawTimelineInfo(raw: RawTimelineInfo): HttpTimelineInfo { + return { + ...raw, + lastModified: new Date(raw.lastModified), + }; +} + function processRawTimelinePostInfo( raw: RawTimelinePostInfo ): HttpTimelinePostInfo; @@ -190,6 +208,19 @@ function processRawTimelinePostInfo( export interface IHttpTimelineClient { listTimeline(query: HttpTimelineListQuery): Promise; getTimeline(timelineName: string): Promise; + getTimeline( + timelineName: string, + query: { + checkUniqueId?: string; + } + ): Promise; + getTimeline( + timelineName: string, + query: { + checkUniqueId?: string; + ifModifiedSince: Date; + } + ): Promise; postTimeline( req: HttpTimelinePostRequest, token: string @@ -256,17 +287,46 @@ export interface IHttpTimelineClient { export class HttpTimelineClient implements IHttpTimelineClient { listTimeline(query: HttpTimelineListQuery): Promise { return axios - .get( + .get( applyQueryParameters(`${apiBaseUrl}/timelines`, query) ) .then(extractResponseData) + .then((list) => list.map(processRawTimelineInfo)) .catch(convertToNetworkError); } - getTimeline(timelineName: string): Promise { + getTimeline(timelineName: string): Promise; + getTimeline( + timelineName: string, + query: { + checkUniqueId?: string; + } + ): Promise; + getTimeline( + timelineName: string, + query: { + checkUniqueId?: string; + ifModifiedSince: Date; + } + ): Promise; + getTimeline( + timelineName: string, + query?: { + checkUniqueId?: string; + ifModifiedSince?: Date; + } + ): Promise { return axios - .get(`${apiBaseUrl}/timelines/${timelineName}`) - .then(extractResponseData) + .get( + applyQueryParameters(`${apiBaseUrl}/timelines/${timelineName}`, query) + ) + .then((res) => { + if (res.status === 304) { + return new NotModified(); + } else { + return processRawTimelineInfo(res.data); + } + }) .catch(convertToIfStatusCodeIs(404, HttpTimelineNotExistError)) .catch(convertToNetworkError); } @@ -276,8 +336,9 @@ export class HttpTimelineClient implements IHttpTimelineClient { token: string ): Promise { return axios - .post(`${apiBaseUrl}/timelines?token=${token}`, req) + .post(`${apiBaseUrl}/timelines?token=${token}`, req) .then(extractResponseData) + .then(processRawTimelineInfo) .catch(convertToIfErrorCodeIs(11040101, HttpTimelineNameConflictError)) .catch(convertToNetworkError); } @@ -288,11 +349,12 @@ export class HttpTimelineClient implements IHttpTimelineClient { token: string ): Promise { return axios - .patch( + .patch( `${apiBaseUrl}/timelines/${timelineName}?token=${token}`, req ) .then(extractResponseData) + .then(processRawTimelineInfo) .catch(convertToNetworkError); } diff --git a/Timeline/ClientApp/src/app/utilities/url.ts b/Timeline/ClientApp/src/app/utilities/url.ts index 0b8623a2..90923fd2 100644 --- a/Timeline/ClientApp/src/app/utilities/url.ts +++ b/Timeline/ClientApp/src/app/utilities/url.ts @@ -34,8 +34,19 @@ export function updateQueryString( } export function applyQueryParameters(url: string, query: T): string { + if (query == null) return url; + for (const [key, value] of Object.entries(query)) { - url = updateQueryString(key, String(value), url); + if (typeof value === 'string') url = updateQueryString(key, value, url); + else if (typeof value === 'number') + url = updateQueryString(key, String(value), url); + else if (typeof value === 'boolean') + url = updateQueryString(key, value ? 'true' : 'false', url); + else if (value instanceof Date) + url = updateQueryString(key, value.toISOString(), url); + else { + console.error('Unknown query parameter type. Param: ', value); + } } return url; } -- cgit v1.2.3