diff --git a/src/OnspringClient.ts b/src/OnspringClient.ts index 8998457..cbcaaa3 100644 --- a/src/OnspringClient.ts +++ b/src/OnspringClient.ts @@ -1,12 +1,12 @@ -import { AxiosInstance } from 'axios'; +import { AxiosInstance, AxiosResponse } from 'axios'; import axios from 'axios'; import { ArgumentValidator } from './models/ArgumentValidator'; import { EndpointFactory } from './models/EndpointFactory'; import { ApiResponseFactory } from './models/ApiResponseFactory'; import { ApiResponse } from './models/ApiResponse'; -import { App } from './models/App'; import { PagingRequest } from './models/PagingRequest'; import { GetPagedAppsResponse } from './models/GetPagedAppsResponse'; +import { App } from './models/App'; /** * @class OnspringClient - A client that can communicate with the Onspring API. @@ -55,14 +55,14 @@ export class OnspringClient { this._client.defaults.baseURL ); - const response = await this.get>(endpoint); + const response = await this.get(endpoint); return response.isSuccessful; } /** - * + * * @param pagingRequest - The paging request that will be used to get the apps. - * @returns + * @returns */ public async getApps( pagingRequest: PagingRequest = new PagingRequest(1, 50) @@ -72,7 +72,8 @@ export class OnspringClient { pagingRequest ); - return await this.get(endpoint); + var apiResponse = await this.get(endpoint); + return apiResponse.AsGetPagedAppsResponseType(); } /** diff --git a/src/models/ApiResponse.ts b/src/models/ApiResponse.ts index e477b21..c005b28 100644 --- a/src/models/ApiResponse.ts +++ b/src/models/ApiResponse.ts @@ -1,3 +1,6 @@ +import { App } from "./App"; +import { GetPagedAppsResponse } from "./GetPagedAppsResponse"; + /** * @class ApiResponse - A generic response object for API requests. */ @@ -5,22 +8,22 @@ export class ApiResponse { /** * @property {number} statusCode - The status code of the response. */ - readonly statusCode: number; + public statusCode: number; /** * @property {boolean} isSuccessful - True if the status code is less than 400; otherwise, false. */ - readonly isSuccessful: boolean; + public isSuccessful: boolean; /** * @property {string} message - The message of the response. */ - readonly message: string; + public message: string; /** * @property {T} data - The data of the response. */ - readonly data: T; + public data: T; /** * @constructor - Creates a new instance of the ApiResponse class. @@ -35,4 +38,25 @@ export class ApiResponse { this.message = message; this.data = data; } + + public AsGetPagedAppsResponseType(): ApiResponse { + var apiResponse = this as ApiResponse; + var apps = apiResponse.data.items.map((item: any) => { + return new App(item.href, item.id, item.name); + }); + + var getAppsPagedResponse = new GetPagedAppsResponse( + apps, + apiResponse.data.pageNumber, + apiResponse.data.pageSize, + apiResponse.data.totalPages, + apiResponse.data.totalRecords + ); + + return new ApiResponse( + apiResponse.statusCode, + apiResponse.message, + getAppsPagedResponse + ); + } } diff --git a/src/models/ApiResponseFactory.ts b/src/models/ApiResponseFactory.ts index 975efba..083dd8e 100644 --- a/src/models/ApiResponseFactory.ts +++ b/src/models/ApiResponseFactory.ts @@ -15,8 +15,7 @@ export class ApiResponseFactory { const message = this.TryToGetMessage(response); if (this.isSuccessStatusCode(response.status) === true) { - const data = response.data as T; - return new ApiResponse(response.status, message, data); + return new ApiResponse(response.status, message, response.data); } return new ApiResponse(response.status, message, null); diff --git a/src/models/GetPagedAppsResponse.ts b/src/models/GetPagedAppsResponse.ts index 02ac269..371e0b5 100644 --- a/src/models/GetPagedAppsResponse.ts +++ b/src/models/GetPagedAppsResponse.ts @@ -2,7 +2,7 @@ import { PagedResponse } from "./PagedResponse"; import { App } from "./App"; export class GetPagedAppsResponse extends PagedResponse { - constructor(items: App[], pageNumber: number, pageSize: number, totalCount: number) { - super(items, pageNumber, pageSize, totalCount); + constructor(items: App[], pageNumber: number, pageSize: number, totalPages: number, totalRecords: number) { + super(items, pageNumber, pageSize, totalPages, totalRecords); } } \ No newline at end of file diff --git a/src/models/PagedResponse.ts b/src/models/PagedResponse.ts index 029fe71..11b89ee 100644 --- a/src/models/PagedResponse.ts +++ b/src/models/PagedResponse.ts @@ -2,12 +2,14 @@ export class PagedResponse { public items: T[]; public pageNumber: number; public pageSize: number; - public totalCount: number; + public totalPages: number; + public totalRecords: number; - constructor(items: T[], pageNumber: number, pageSize: number, totalCount: number) { + constructor(items: T[], pageNumber: number, pageSize: number, totalPages: number, totalRecords: number) { this.items = items; this.pageNumber = pageNumber; this.pageSize = pageSize; - this.totalCount = totalCount; + this.totalPages = totalPages; + this.totalRecords = totalRecords; } } \ No newline at end of file diff --git a/tests/EndpointFactory.spec.ts b/tests/EndpointFactory.spec.ts index 9722e45..2375c14 100644 --- a/tests/EndpointFactory.spec.ts +++ b/tests/EndpointFactory.spec.ts @@ -15,7 +15,7 @@ describe('EndpointFactory', function () { describe('getAppsEndpoint', function () { it('should return the correct apps endpoint with paging params based on paging request parameter passed', function () { const result = EndpointFactory.getAppsEndpoint(baseUrl, new PagingRequest(2, 1000)); - expect(result).to.equal(`${baseUrl}/Apps?page=2&pageSize=1000`); + expect(result).to.equal(`${baseUrl}/Apps?pageSize=1000&pageNumber=2`); }); }); diff --git a/tests/GetPagedAppsResponse.spec.ts b/tests/GetPagedAppsResponse.spec.ts index 2fbf2a8..72ec4d4 100644 --- a/tests/GetPagedAppsResponse.spec.ts +++ b/tests/GetPagedAppsResponse.spec.ts @@ -12,17 +12,18 @@ describe('GetPagedAppsResponse', function () { }); it('should have 4 parameters', function () { - expect(GetPagedAppsResponse).to.have.lengthOf(4); + expect(GetPagedAppsResponse).to.have.lengthOf(5); }); it('should construct a new instance of GetPagedAppsResponse', function () { - const getPagedAppsResponse = new GetPagedAppsResponse([new App('test', 1, 'test'), new App('test', 1, 'test')], 1, 10, 100); + const getPagedAppsResponse = new GetPagedAppsResponse([new App('test', 1, 'test'), new App('test', 1, 'test')], 1, 10, 100, 100); expect(getPagedAppsResponse).to.not.be.undefined; expect(getPagedAppsResponse).to.be.instanceOf(GetPagedAppsResponse); expect(getPagedAppsResponse).to.have.property('items').to.be.an('array').to.have.lengthOf(2); expect(getPagedAppsResponse).to.have.property('pageNumber').to.be.a('number').to.equal(1); expect(getPagedAppsResponse).to.have.property('pageSize').to.be.a('number').to.equal(10); - expect(getPagedAppsResponse).to.have.property('totalCount').to.be.a('number').to.equal(100); + expect(getPagedAppsResponse).to.have.property('totalPages').to.be.a('number').to.equal(100); + expect(getPagedAppsResponse).to.have.property('totalRecords').to.be.a('number').to.equal(100); }); }); \ No newline at end of file diff --git a/tests/OnspringClient.spec.ts b/tests/OnspringClient.spec.ts index 7e74af2..7395989 100644 --- a/tests/OnspringClient.spec.ts +++ b/tests/OnspringClient.spec.ts @@ -3,6 +3,8 @@ import { ApiResponse } from '../src/models/ApiResponse'; import axios, { AxiosResponse, InternalAxiosRequestConfig } from 'axios'; import { expect } from 'chai'; import * as sinon from 'sinon'; +import { GetPagedAppsResponse } from '../src/models/GetPagedAppsResponse'; +import { App } from '../src/models/App'; describe('OnspringClient', function () { const baseUrl = 'https://api.onspring.dev'; @@ -109,22 +111,16 @@ describe('OnspringClient', function () { it('should return a promise that resolves to a boolean', async function () { const client = new OnspringClient(baseUrl, apiKey); - const result = await client.canConnect(); - expect(result).to.be.a('boolean'); - }); - - it('should return a promise that resolves to true when able to connect to the Onspring API', async function () { - const client = new OnspringClient(baseUrl, apiKey); - - const mockClient = axios.create({ + + const mockAxiosClient = axios.create({ baseURL: baseUrl, headers: { 'x-apikey': apiKey, 'x-api-version': '2', }, }); - - sinon.stub(mockClient, 'get').returns(Promise.resolve({ + + sinon.stub(mockAxiosClient, 'get').returns(Promise.resolve({ status: 200, statusText: 'OK', data: null, @@ -132,7 +128,32 @@ describe('OnspringClient', function () { config: {} as InternalAxiosRequestConfig, } as AxiosResponse)); - sinon.stub(client, '_client' as any).value(mockClient); + sinon.stub(client, '_client' as any).value(mockAxiosClient); + + const result = await client.canConnect(); + expect(result).to.be.a('boolean'); + }); + + it('should return a promise that resolves to true when able to connect to the Onspring API', async function () { + const client = new OnspringClient(baseUrl, apiKey); + + const mockAxiosClient = axios.create({ + baseURL: baseUrl, + headers: { + 'x-apikey': apiKey, + 'x-api-version': '2', + }, + }); + + sinon.stub(mockAxiosClient, 'get').returns(Promise.resolve({ + status: 200, + statusText: 'OK', + data: null, + headers: {}, + config: {} as InternalAxiosRequestConfig, + } as AxiosResponse)); + + sinon.stub(client, '_client' as any).value(mockAxiosClient); const result = await client.canConnect(); expect(result).to.be.true; @@ -140,8 +161,8 @@ describe('OnspringClient', function () { it('should return a promise that resolves to false when unable to connect to the Onspring API', async function () { const client = new OnspringClient(baseUrl, apiKey); - // sinon.stub(client, 'get' as any).returns(Promise.resolve(new ApiResponse(500, 'Internal Server Error', null))); - const mockClient = axios.create({ + + const mockAxiosClient = axios.create({ baseURL: baseUrl, headers: { 'x-apikey': apiKey, @@ -149,7 +170,7 @@ describe('OnspringClient', function () { }, }); - sinon.stub(mockClient, 'get').returns(Promise.reject({ + sinon.stub(mockAxiosClient, 'get').returns(Promise.resolve({ status: 500, statusText: 'Internal Server Error', data: null, @@ -157,10 +178,86 @@ describe('OnspringClient', function () { config: {} as InternalAxiosRequestConfig, } as AxiosResponse)); - sinon.stub(client, '_client' as any).value(mockClient); + sinon.stub(client, '_client' as any).value(mockAxiosClient); const result = await client.canConnect(); expect(result).to.be.false; }); }); + + describe('getApps', function () { + it('should be defined', function () { + expect(new OnspringClient(baseUrl, apiKey).getApps).to.not.be.undefined; + }); + + it('should be a function', function () { + expect(new OnspringClient(baseUrl, apiKey).getApps).to.be.a('function'); + }); + + it('should have 0 parameters', function () { + expect(new OnspringClient(baseUrl, apiKey).getApps).to.have.lengthOf(0); + }); + + it('should return a promise', function () { + expect(new OnspringClient(baseUrl, apiKey).getApps()).to.be.a('promise'); + }); + + it('should return a promise that resolves to a paged response of apps when request is successful', async function () { + const client = new OnspringClient(baseUrl, apiKey); + + const mockAxiosClient = axios.create({ + baseURL: baseUrl, + headers: { + 'x-apikey': apiKey, + 'x-api-version': '2', + }, + }); + + sinon.stub(mockAxiosClient, 'get').returns( + Promise.resolve({ + status: 200, + statusText: 'OK', + data: { + pageNumber: 1, + pageSize: 2, + totalPages: 1, + totalRecords: 2, + items: [ + { + href: 'https://api.onspring.dev/Apps/id/1', + id: '1', + name: 'Test App 1', + }, + { + href: 'https://api.onspring.dev/Apps/id/2', + id: '2', + name: 'Test App 2', + }, + ], + }, + headers: {}, + config: {} as InternalAxiosRequestConfig, + } as AxiosResponse) + ); + + sinon.stub(client, '_client' as any).value(mockAxiosClient); + + const result = await client.getApps(); + expect(result).to.be.instanceOf(ApiResponse); + expect(result).to.have.property('statusCode', 200); + expect(result).to.have.property('isSuccessful', true); + expect(result).to.have.property('message', ''); + expect(result).to.have.property('data'); + expect(result.data).to.be.instanceOf(GetPagedAppsResponse); + expect(result.data).to.have.property('pageNumber', 1); + expect(result.data).to.have.property('pageSize', 2); + expect(result.data).to.have.property('totalPages', 1); + expect(result.data).to.have.property('totalRecords', 2); + expect(result.data).to.have.property('items'); + expect(result.data.items).to.be.instanceOf(Array); + expect(result.data.items).to.have.lengthOf(2); + expect(result.data.items[0]).to.be.instanceOf(App); + expect(result.data.items[1]).to.be.instanceOf(App); + }); + }); }); diff --git a/tests/PagedResponse.spec.ts b/tests/PagedResponse.spec.ts index 68ba643..f791a49 100644 --- a/tests/PagedResponse.spec.ts +++ b/tests/PagedResponse.spec.ts @@ -11,17 +11,18 @@ describe('PagedResponse', function () { }); it('should have 4 parameters', function () { - expect(PagedResponse).to.have.lengthOf(4); + expect(PagedResponse).to.have.lengthOf(5); }); it('should construct a new instance of PagedResponse', function () { - const pagedResponse = new PagedResponse([1, 2, 3, 4], 1, 10, 100); + const pagedResponse = new PagedResponse([1, 2, 3, 4], 1, 10, 100, 100); expect(pagedResponse).to.not.be.undefined; expect(pagedResponse).to.be.instanceOf(PagedResponse); expect(pagedResponse).to.have.property('items').to.be.an('array').to.have.lengthOf(4); expect(pagedResponse).to.have.property('pageNumber').to.be.a('number').to.equal(1); expect(pagedResponse).to.have.property('pageSize').to.be.a('number').to.equal(10); - expect(pagedResponse).to.have.property('totalCount').to.be.a('number').to.equal(100); + expect(pagedResponse).to.have.property('totalPages').to.be.a('number').to.equal(100); + expect(pagedResponse).to.have.property('totalRecords').to.be.a('number').to.equal(100); }); });