From 557a8dbce03388b63429980bc2d7a8451ab3c440 Mon Sep 17 00:00:00 2001 From: Kevin Berry <41717340+51ngul4r1ty@users.noreply.github.com> Date: Wed, 11 May 2022 09:45:48 -0400 Subject: [PATCH 1/5] fixed canonical host url setting logic --- package.json | 2 +- src/atollClient.ts | 21 ++++++++++++--------- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/package.json b/package.json index 98b0814..2835fbc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@atoll/client-sdk", - "version": "0.18.1", + "version": "0.19.1", "description": "Atoll REST API client SDK", "main": "dist/index.cjs.js", "module": "dist/index.es.js", diff --git a/src/atollClient.ts b/src/atollClient.ts index d3e7540..319a13f 100644 --- a/src/atollClient.ts +++ b/src/atollClient.ts @@ -162,25 +162,31 @@ export class AtollClient { password: string, notificationHandler: HostNotificationHandler ): Promise => { - const updateCanonicalHostBaseUrl = true; return this.commonConnect( "connect", hostBaseUrl, notificationHandler, { skipRetryOnAuthFailure: true }, - updateCanonicalHostBaseUrl, () => this.lookupRelativeUriFromApiMap("user-auth", "action"), () => ({ username, password }) ); }; - public connectWithRefreshToken = async (hostBaseUrl: string, notificationHandler: HostNotificationHandler): Promise => { - const updateCanonicalHostBaseUrl = false; + /** + * After "connect" has been used the client can cache the refresh token and use that to reconnect. "refreshToken" is a public + * field that can/should be set before calling this. + * @param hostBaseUrl + * @param notificationHandler + * @returns + */ + public reconnect = async (hostBaseUrl: string, notificationHandler: HostNotificationHandler): Promise => { + if (!this.refreshToken) { + throw new Error("refreshToken should be set before using 'reconnect'"); + } return this.commonConnect( "connect", hostBaseUrl, notificationHandler, {}, - updateCanonicalHostBaseUrl, () => this.lookupRelativeUriFromApiMap("refresh-token", "action"), () => ({ refreshToken: this.refreshToken }) ); @@ -190,7 +196,6 @@ export class AtollClient { hostBaseUrl: string, notificationHandler: HostNotificationHandler, authOptions: any, - updateCanonicalHostBaseUrl: boolean, getAuthUrl: () => string, buildAuthPayload: () => any ): Promise => { @@ -217,9 +222,7 @@ export class AtollClient { const { authToken, refreshToken } = authServerResponse.data.item; restApi.setDefaultHeader("Authorization", `Bearer ${authToken}`); this.refreshToken = refreshToken; - if (updateCanonicalHostBaseUrl) { - this.canonicalHostBaseUrl = canonicalHostBaseUrl; - } + this.canonicalHostBaseUrl = canonicalHostBaseUrl; return null; } catch (error) { this.connecting = false; From 425f8d02d14a85491dd3c5237bf18338c8ebec43 Mon Sep 17 00:00:00 2001 From: Kevin Berry <41717340+51ngul4r1ty@users.noreply.github.com> Date: Wed, 11 May 2022 10:00:20 -0400 Subject: [PATCH 2/5] should not have been public --- src/atollClient.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/atollClient.ts b/src/atollClient.ts index 319a13f..718f97d 100644 --- a/src/atollClient.ts +++ b/src/atollClient.ts @@ -191,7 +191,7 @@ export class AtollClient { () => ({ refreshToken: this.refreshToken }) ); }; - public commonConnect = async ( + private commonConnect = async ( connectFunctionName: string, hostBaseUrl: string, notificationHandler: HostNotificationHandler, From 2f166cad8938a3c8f7aff0622e59a7784a9e3da2 Mon Sep 17 00:00:00 2001 From: Kevin Berry <41717340+51ngul4r1ty@users.noreply.github.com> Date: Wed, 11 May 2022 10:05:28 -0400 Subject: [PATCH 3/5] refactor (just re-organizing - no logic change) --- src/atollClient.ts | 191 +++++++++++++++++++++++---------------------- 1 file changed, 99 insertions(+), 92 deletions(-) diff --git a/src/atollClient.ts b/src/atollClient.ts index 718f97d..4130aee 100644 --- a/src/atollClient.ts +++ b/src/atollClient.ts @@ -37,40 +37,19 @@ export class AtollClient { * @param hostBaseUrl this is needed because this particular API call happens before the canonicalHostBaseUrl is set * @returns An API Map list. */ - private getApiMap = async (hostBaseUrl: string): Promise => { - const result = await restApi.get(`${hostBaseUrl}${MAP_RELATIVE_URL}`); - return result.data.items; - }; - private lookupUriFromApiMap = (id: string, rel: string): string => { - if (!this.canonicalHostBaseUrl) { - throw new Error("Unable to look up a URI without the host base URL being set first!"); - } - const uri = this.lookupRelativeUriFromApiMap(id, rel); - return `${this.canonicalHostBaseUrl}${uri}`; - }; - private lookupRelativeUriFromApiMap = (id: string, rel: string): string => { - if (!this.apiMap) { - throw new Error("API Map needs to be retrieved first!"); - } - const endpointMapItem = this.apiMap[id]; - if (!endpointMapItem) { - throw new Error(`Unable to find API Map Item with ID "${id}"`); - } - const matchingActions = endpointMapItem.links.filter((link) => link.rel === rel); - if (matchingActions.length === 0) { - throw new Error(`Unable to find a matching API Map Item Link with rel "${rel}"`); - } - if (matchingActions.length > 1) { - throw new Error(`Found ${matchingActions.length} matching API Map Item Links with rel "${rel}"`); - } - return matchingActions[0].uri; - }; - private fetchApiMapAndBuildIndex = async (hostBaseUrl: string) => { - const apiMapResponse = await this.getApiMap(hostBaseUrl); - this.apiMap = {}; - apiMapResponse.forEach((apiMapItem) => { - this.apiMap[apiMapItem.id] = apiMapItem; - }); + // #region notification handler related + private setupAuthFailureHandler = (refreshTokenUri: string) => { + restApi.onAuthFailure = async () => { + await this.onAuthFailureNotification(refreshTokenUri); + try { + const { authToken, refreshToken } = await this.fetchAuthTokenUsingRefreshToken(refreshTokenUri); + await this.onRefreshTokenSuccessNotification(authToken, refreshToken); + return true; + } catch (error) { + await this.onRefreshTokenFailureNotification(this.refreshToken); + return false; + } + }; }; private onAuthFailureNotification = async (refreshTokenUri: string): Promise => { if (this.notificationHandler) { @@ -87,6 +66,8 @@ export class AtollClient { await this.notificationHandler("Unable to re-connect to Atoll Server - trying signing in again", "warn"); } }; + // #endregion + // #region auth related private fetchAuthTokenUsingRefreshToken = async ( refreshTokenUri: string ): Promise<{ authToken: string; refreshToken: string }> => { @@ -103,19 +84,6 @@ export class AtollClient { refreshToken }; }; - private setupAuthFailureHandler = (refreshTokenUri: string) => { - restApi.onAuthFailure = async () => { - await this.onAuthFailureNotification(refreshTokenUri); - try { - const { authToken, refreshToken } = await this.fetchAuthTokenUsingRefreshToken(refreshTokenUri); - await this.onRefreshTokenSuccessNotification(authToken, refreshToken); - return true; - } catch (error) { - await this.onRefreshTokenFailureNotification(this.refreshToken); - return false; - } - }; - }; private setupTokenRefresher = (canonicalHostBaseUrl?: string) => { let refreshTokenUri: string; if (canonicalHostBaseUrl) { @@ -126,29 +94,8 @@ export class AtollClient { } this.setupAuthFailureHandler(refreshTokenUri); }; - private canonicalizeUrl = (url: string): string => { - return url.endsWith("/") ? url.substring(0, url.length - 1) : url; - }; - - private buildErrorResult = (error: any, functionName: string) => { - const errorTyped = error as RestApiFetchError; - if (errorTyped.errorType === RestApiFetchErrorType.UnexpectedError) { - throw new Error(`${errorTyped.message} (${functionName})`); - } - const status = errorTyped.status; - const message = errorTyped.message; - return `Atoll REST API error: ${status} - ${message} (${functionName})`; - }; - - public buildUriFromBaseAndRelative = (baseUri: string, relativeUri: string): string => { - return `${baseUri}${relativeUri}`; - }; - public buildFullUri = (relativeUri: string): string => { - if (!this.canonicalHostBaseUrl) { - throw new Error("buildFullUri requires canonicalHostBaseUrl to be set first"); - } - return this.buildUriFromBaseAndRelative(this.canonicalHostBaseUrl, relativeUri); - }; + // #endregion + // #region connection related /** * Authenticate user on the provided Atoll host server. * @param hostBaseUrl for example, https://atoll.yourdomain.com/ @@ -229,7 +176,6 @@ export class AtollClient { return this.buildErrorResult(error, connectFunctionName); } }; - public disconnect = () => { if (this.isConnecting()) { throw new Error("Unable to disconnect while connection is being established!"); @@ -244,16 +190,94 @@ export class AtollClient { public isConnected = (): boolean => { return !!this.refreshToken; }; - public fetchProjects = async (): Promise => { - const projectsUri = this.lookupUriFromApiMap("projects", "collection"); - const result = await restApi.get(projectsUri); + // #endregion + // #region utils + private getApiMap = async (hostBaseUrl: string): Promise => { + const result = await restApi.get(`${hostBaseUrl}${MAP_RELATIVE_URL}`); return result.data.items; }; + private lookupUriFromApiMap = (id: string, rel: string): string => { + if (!this.canonicalHostBaseUrl) { + throw new Error("Unable to look up a URI without the host base URL being set first!"); + } + const uri = this.lookupRelativeUriFromApiMap(id, rel); + return `${this.canonicalHostBaseUrl}${uri}`; + }; + private lookupRelativeUriFromApiMap = (id: string, rel: string): string => { + if (!this.apiMap) { + throw new Error("API Map needs to be retrieved first!"); + } + const endpointMapItem = this.apiMap[id]; + if (!endpointMapItem) { + throw new Error(`Unable to find API Map Item with ID "${id}"`); + } + const matchingActions = endpointMapItem.links.filter((link) => link.rel === rel); + if (matchingActions.length === 0) { + throw new Error(`Unable to find a matching API Map Item Link with rel "${rel}"`); + } + if (matchingActions.length > 1) { + throw new Error(`Found ${matchingActions.length} matching API Map Item Links with rel "${rel}"`); + } + return matchingActions[0].uri; + }; + private fetchApiMapAndBuildIndex = async (hostBaseUrl: string) => { + const apiMapResponse = await this.getApiMap(hostBaseUrl); + this.apiMap = {}; + apiMapResponse.forEach((apiMapItem) => { + this.apiMap[apiMapItem.id] = apiMapItem; + }); + }; + private canonicalizeUrl = (url: string): string => { + return url.endsWith("/") ? url.substring(0, url.length - 1) : url; + }; + private buildErrorResult = (error: any, functionName: string) => { + const errorTyped = error as RestApiFetchError; + if (errorTyped.errorType === RestApiFetchErrorType.UnexpectedError) { + throw new Error(`${errorTyped.message} (${functionName})`); + } + const status = errorTyped.status; + const message = errorTyped.message; + return `Atoll REST API error: ${status} - ${message} (${functionName})`; + }; + public buildUriFromBaseAndRelative = (baseUri: string, relativeUri: string): string => { + return `${baseUri}${relativeUri}`; + }; + public buildFullUri = (relativeUri: string): string => { + if (!this.canonicalHostBaseUrl) { + throw new Error("buildFullUri requires canonicalHostBaseUrl to be set first"); + } + return this.buildUriFromBaseAndRelative(this.canonicalHostBaseUrl, relativeUri); + }; private checkValidFullUri = (functionName: string, uri: string) => { if (!isValidFullUri(uri)) { throw new Error(`Invalid URI ${uri} passed to ${functionName}`); } }; + public findLinkByRel = (links: ApiResourceItemLink[], rel: string): ApiResourceItemLink | null => { + const matchingLinks = links.filter((link) => link.rel === rel); + const matchingLinkCount = matchingLinks.length; + if (matchingLinkCount === 0) { + return null; + } else if (matchingLinkCount > 1) { + throw new Error(`findLinkUrlByRel(links, "${rel}") matched ${matchingLinkCount} - expecting 1!`); + } else { + return matchingLinks[0]; + } + }; + public findLinkUriByRel = (links: ApiResourceItemLink[], rel: string): string | null => { + const link = this.findLinkByRel(links, rel); + if (link === null) { + return null; + } + return link.uri; + }; + // #endregion + // #region manipulating resources + public fetchProjects = async (): Promise => { + const projectsUri = this.lookupUriFromApiMap("projects", "collection"); + const result = await restApi.get(projectsUri); + return result.data.items; + }; public fetchSprintByUri = async (sprintUri: string): Promise => { this.checkValidFullUri("fetchSprintByUri", sprintUri); try { @@ -273,22 +297,5 @@ export class AtollClient { const result = await restApi.get(sprintBacklogItemsUri); return result.data.items; }; - public findLinkByRel = (links: ApiResourceItemLink[], rel: string): ApiResourceItemLink | null => { - const matchingLinks = links.filter((link) => link.rel === rel); - const matchingLinkCount = matchingLinks.length; - if (matchingLinkCount === 0) { - return null; - } else if (matchingLinkCount > 1) { - throw new Error(`findLinkUrlByRel(links, "${rel}") matched ${matchingLinkCount} - expecting 1!`); - } else { - return matchingLinks[0]; - } - }; - public findLinkUriByRel = (links: ApiResourceItemLink[], rel: string): string | null => { - const link = this.findLinkByRel(links, rel); - if (link === null) { - return null; - } - return link.uri; - }; + // #endregion } From d4dc3c1044dd15edee6e32d74ce3d53c8fdb1958 Mon Sep 17 00:00:00 2001 From: Kevin Berry <41717340+51ngul4r1ty@users.noreply.github.com> Date: Sat, 14 May 2022 18:51:51 -0400 Subject: [PATCH 4/5] added mapping utils etc. --- package-lock.json | 47 +++++++++++++++++---------- package.json | 11 +++++-- src/atollClient.ts | 19 ++++++++--- src/index.ts | 1 + src/mappers.ts | 79 ++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 134 insertions(+), 23 deletions(-) create mode 100644 src/mappers.ts diff --git a/package-lock.json b/package-lock.json index 5d0b9cd..3739342 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,16 +1,17 @@ { "name": "@atoll/client-sdk", - "version": "0.17.2", + "version": "0.22.2", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@atoll/client-sdk", - "version": "0.17.2", + "version": "0.22.2", "license": "MIT", "dependencies": { - "@atoll/api-types": "0.6.1", - "@atoll/rest-fetch": "0.9.0", + "@atoll/api-types": "1.0.0", + "@atoll/rest-fetch": "1.1.0", + "@atoll/rich-types": "0.1.1", "http-status-codes": "2.2.0" }, "devDependencies": { @@ -52,18 +53,18 @@ } }, "node_modules/@atoll/api-types": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/@atoll/api-types/-/api-types-0.6.1.tgz", - "integrity": "sha512-I6wCAz3th8shPXzrCusxDJWBxbdQa+eiSrrIRz6TWXbbQwhIqoggnZV07O2NbU0hgdKUXSBfk0om1nEKgAyQWQ==", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@atoll/api-types/-/api-types-1.0.0.tgz", + "integrity": "sha512-dDxeYYSA8hBZ/V//2z9DXwj1jOTEyQAwVdQSmGJ1yrIs4wQ9P8xpeD5B/YdYDYvVD/40WevaV17BqrbmpmRA3Q==", "engines": { "node": ">=16.14.0", "npm": ">=8.3.1" } }, "node_modules/@atoll/rest-fetch": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/@atoll/rest-fetch/-/rest-fetch-0.9.0.tgz", - "integrity": "sha512-Yazsdc/vyBXZwk6niqgB9y1NPI5n/L+t80lmHO1PTKLb2Ys4n6WKeNr+aLNWT+7I0g0bG+LbzjowtXXE91iuxA==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@atoll/rest-fetch/-/rest-fetch-1.1.0.tgz", + "integrity": "sha512-MKAJDZ/ghIy+4omsizTHNTi/9gPMWiVUJhddoCWo0yx6RHL7UHakvBl9Hh0d4V0NsMh0hspAn0j6ACQHzcKu1Q==", "dependencies": { "http-status-codes": "2.2.0" }, @@ -75,6 +76,15 @@ "axios": "0.27.2" } }, + "node_modules/@atoll/rich-types": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@atoll/rich-types/-/rich-types-0.1.1.tgz", + "integrity": "sha512-v7ik6UCy9tZ9SiKxOlCJGc5X7eU3MA1QplnDXaszWMt1AR/48xpiH6ddH5aAcGNcH1ph3FLGmPEQQynBosewMA==", + "engines": { + "node": ">=16.14.0", + "npm": ">=8.3.1" + } + }, "node_modules/@babel/code-frame": { "version": "7.16.7", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.7.tgz", @@ -6709,18 +6719,23 @@ } }, "@atoll/api-types": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/@atoll/api-types/-/api-types-0.6.1.tgz", - "integrity": "sha512-I6wCAz3th8shPXzrCusxDJWBxbdQa+eiSrrIRz6TWXbbQwhIqoggnZV07O2NbU0hgdKUXSBfk0om1nEKgAyQWQ==" + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@atoll/api-types/-/api-types-1.0.0.tgz", + "integrity": "sha512-dDxeYYSA8hBZ/V//2z9DXwj1jOTEyQAwVdQSmGJ1yrIs4wQ9P8xpeD5B/YdYDYvVD/40WevaV17BqrbmpmRA3Q==" }, "@atoll/rest-fetch": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/@atoll/rest-fetch/-/rest-fetch-0.9.0.tgz", - "integrity": "sha512-Yazsdc/vyBXZwk6niqgB9y1NPI5n/L+t80lmHO1PTKLb2Ys4n6WKeNr+aLNWT+7I0g0bG+LbzjowtXXE91iuxA==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@atoll/rest-fetch/-/rest-fetch-1.1.0.tgz", + "integrity": "sha512-MKAJDZ/ghIy+4omsizTHNTi/9gPMWiVUJhddoCWo0yx6RHL7UHakvBl9Hh0d4V0NsMh0hspAn0j6ACQHzcKu1Q==", "requires": { "http-status-codes": "2.2.0" } }, + "@atoll/rich-types": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@atoll/rich-types/-/rich-types-0.1.1.tgz", + "integrity": "sha512-v7ik6UCy9tZ9SiKxOlCJGc5X7eU3MA1QplnDXaszWMt1AR/48xpiH6ddH5aAcGNcH1ph3FLGmPEQQynBosewMA==" + }, "@babel/code-frame": { "version": "7.16.7", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.7.tgz", diff --git a/package.json b/package.json index 2835fbc..f4ad160 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@atoll/client-sdk", - "version": "0.19.1", + "version": "1.0.0", "description": "Atoll REST API client SDK", "main": "dist/index.cjs.js", "module": "dist/index.es.js", @@ -9,6 +9,10 @@ "dist" ], "sideEffects": false, + "publishConfig": { + "access": "public", + "registry": "https://registry.npmjs.org/" + }, "scripts": { "transpile": "tsc", "build": "rollup -c", @@ -58,8 +62,9 @@ "typescript": "4.6.4" }, "dependencies": { - "@atoll/api-types": "0.6.1", - "@atoll/rest-fetch": "0.9.0", + "@atoll/api-types": "1.0.0", + "@atoll/rest-fetch": "1.1.0", + "@atoll/rich-types": "0.1.1", "http-status-codes": "2.2.0" } } diff --git a/src/atollClient.ts b/src/atollClient.ts index 4130aee..c41bdb3 100644 --- a/src/atollClient.ts +++ b/src/atollClient.ts @@ -9,6 +9,7 @@ import type { AuthServerResponse, ProjectResourceItem, ProjectsServerResponse, + SprintBacklogItemServerResponse, SprintBacklogItemsServerResponse, SprintBacklogResourceItem, SprintResourceItem, @@ -21,6 +22,8 @@ import { RestApiFetchErrorType } from "@atoll/rest-fetch"; // utils import { restApi } from "./restApi"; import { isValidFullUri } from "./validationUtils"; +import { backlogItemStatusToString } from "./mappers"; +import { BacklogItemStatus } from "@atoll/rich-types"; export const LOGIN_RELATIVE_URL = "/api/v1/actions/login"; export const MAP_RELATIVE_URL = "/api/v1"; @@ -193,7 +196,7 @@ export class AtollClient { // #endregion // #region utils private getApiMap = async (hostBaseUrl: string): Promise => { - const result = await restApi.get(`${hostBaseUrl}${MAP_RELATIVE_URL}`); + const result = await restApi.read(`${hostBaseUrl}${MAP_RELATIVE_URL}`); return result.data.items; }; private lookupUriFromApiMap = (id: string, rel: string): string => { @@ -275,13 +278,13 @@ export class AtollClient { // #region manipulating resources public fetchProjects = async (): Promise => { const projectsUri = this.lookupUriFromApiMap("projects", "collection"); - const result = await restApi.get(projectsUri); + const result = await restApi.read(projectsUri); return result.data.items; }; public fetchSprintByUri = async (sprintUri: string): Promise => { this.checkValidFullUri("fetchSprintByUri", sprintUri); try { - const result = await restApi.get(sprintUri); + const result = await restApi.read(sprintUri); return result.data.item; } catch (err) { if (isRestApiFetchError(err)) { @@ -294,8 +297,16 @@ export class AtollClient { }; public fetchSprintBacklogItemsByUri = async (sprintBacklogItemsUri: string): Promise => { this.checkValidFullUri("fetchSprintBacklogItemsByUri", sprintBacklogItemsUri); - const result = await restApi.get(sprintBacklogItemsUri); + const result = await restApi.read(sprintBacklogItemsUri); return result.data.items; }; + public updateBacklogItemPartStatusByUri = async (backlogItemPartUri: string, status: BacklogItemStatus) => { + this.checkValidFullUri("updateBacklogItemPartStatusByUri", backlogItemPartUri); + const payload = { + status: backlogItemStatusToString(status) + }; + const result = await restApi.patch(backlogItemPartUri, payload); + return result.data.item; + }; // #endregion } diff --git a/src/index.ts b/src/index.ts index c3bdb3d..ef36270 100644 --- a/src/index.ts +++ b/src/index.ts @@ -2,5 +2,6 @@ import { AtollClient } from "./atollClient"; export * from "./atollClientTypes"; +export * from "./mappers"; export const atollClient = new AtollClient(); diff --git a/src/mappers.ts b/src/mappers.ts new file mode 100644 index 0000000..e7298d4 --- /dev/null +++ b/src/mappers.ts @@ -0,0 +1,79 @@ +// consts/enums +import { BacklogItemStatus } from "@atoll/rich-types"; + +export const backlogItemStatusToString = (backlogItemStatus: BacklogItemStatus): string | null => { + switch (backlogItemStatus) { + case BacklogItemStatus.NotStarted: { + return "N"; + } + case BacklogItemStatus.InProgress: { + return "P"; + } + case BacklogItemStatus.Done: { + return "D"; + } + case BacklogItemStatus.Accepted: { + return "A"; + } + case BacklogItemStatus.Released: { + return "R"; + } + case BacklogItemStatus.None: { + return null; + } + default: { + throw new Error(`Invalid BacklogItemStatus value ${backlogItemStatus} - unable to map to string`); + } + } +}; + +export const mapBacklogItemToApiStatus = (backlogItemStatus: BacklogItemStatus): string | null => { + switch (backlogItemStatus) { + case BacklogItemStatus.NotStarted: { + return "N"; + } + case BacklogItemStatus.InProgress: { + return "P"; + } + case BacklogItemStatus.Done: { + return "D"; + } + case BacklogItemStatus.Accepted: { + return "A"; + } + case BacklogItemStatus.Released: { + return "R"; + } + case BacklogItemStatus.None: { + return null; + } + default: { + throw new Error(`Invalid BacklogItemStatus value ${backlogItemStatus} - unable to map to string`); + } + } +}; + +export const mapApiToBacklogItemStatus = (status: string | null): BacklogItemStatus => { + switch (status) { + case undefined: + case null: + case "N": { + return BacklogItemStatus.NotStarted; + } + case "P": { + return BacklogItemStatus.InProgress; + } + case "D": { + return BacklogItemStatus.Done; + } + case "A": { + return BacklogItemStatus.Accepted; + } + case "R": { + return BacklogItemStatus.Released; + } + default: { + throw new Error(`Unknown backlog item status "${status}"`); + } + } +}; From d2a72e3d66b7010621aa4ef6930d4de72669e6cf Mon Sep 17 00:00:00 2001 From: Kevin Berry <41717340+51ngul4r1ty@users.noreply.github.com> Date: Tue, 23 Aug 2022 12:57:36 -0400 Subject: [PATCH 5/5] clean up --- src/atollClient.ts | 7 ++++--- src/mappers.ts | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/atollClient.ts b/src/atollClient.ts index c41bdb3..16b10c5 100644 --- a/src/atollClient.ts +++ b/src/atollClient.ts @@ -15,15 +15,16 @@ import type { SprintResourceItem, SprintServerResponse } from "@atoll/api-types"; -import { isRestApiFetchError, RestApiFetchError } from "@atoll/rest-fetch"; +import { BacklogItemStatus } from "@atoll/rich-types"; +import { isRestApiFetchError, RestApiFetchError, RestApiFetchErrorType } from "@atoll/rest-fetch"; + +// interfaces/types import type { HostNotificationHandler } from "./atollClientTypes"; -import { RestApiFetchErrorType } from "@atoll/rest-fetch"; // utils import { restApi } from "./restApi"; import { isValidFullUri } from "./validationUtils"; import { backlogItemStatusToString } from "./mappers"; -import { BacklogItemStatus } from "@atoll/rich-types"; export const LOGIN_RELATIVE_URL = "/api/v1/actions/login"; export const MAP_RELATIVE_URL = "/api/v1"; diff --git a/src/mappers.ts b/src/mappers.ts index e7298d4..605d851 100644 --- a/src/mappers.ts +++ b/src/mappers.ts @@ -1,4 +1,4 @@ -// consts/enums +// libraries import { BacklogItemStatus } from "@atoll/rich-types"; export const backlogItemStatusToString = (backlogItemStatus: BacklogItemStatus): string | null => {