From a1d3683603acce14191383d40cbc9414f6bcd9fe Mon Sep 17 00:00:00 2001 From: StevanFreeborn Date: Thu, 2 Feb 2023 21:37:40 -0600 Subject: [PATCH] feat: implement getFieldById method --- src/models/ApiResponse.ts | 25 ++++- src/models/Field.ts | 38 ++++++++ src/models/OnspringClient.ts | 12 +++ tests/ApiResponse.spec.ts | 41 +++++++++ tests/Field.spec.ts | 53 +++++++++++ tests/OnspringClient.spec.ts | 173 +++++++++++++++++++++++++++++++++++ 6 files changed, 340 insertions(+), 2 deletions(-) create mode 100644 src/models/Field.ts create mode 100644 tests/Field.spec.ts diff --git a/src/models/ApiResponse.ts b/src/models/ApiResponse.ts index a098815..ac2f4d2 100644 --- a/src/models/ApiResponse.ts +++ b/src/models/ApiResponse.ts @@ -1,5 +1,6 @@ import { App } from './App'; import { CollectionResponse } from './CollectionResponse'; +import { Field } from './Field'; import { GetPagedAppsResponse } from './GetPagedAppsResponse'; /** @@ -70,7 +71,7 @@ export class ApiResponse { * @method AsAppType - Converts the ApiResponse to an ApiResponse. * @returns {ApiResponse} - An ApiResponse. */ - AsAppType(): ApiResponse { + public AsAppType(): ApiResponse { const apiResponse = this as ApiResponse; const app = new App( @@ -90,7 +91,7 @@ export class ApiResponse { * @method AsAppCollectionType - Converts the ApiResponse to an ApiResponse>. * @returns {ApiResponse>} - An ApiResponse>. */ - AsAppCollectionType(): ApiResponse> { + public AsAppCollectionType(): ApiResponse> { const apiResponse = this as ApiResponse; const apps = apiResponse.data.items.map((item: any) => { @@ -108,4 +109,24 @@ export class ApiResponse { collectionResponse ); } + + public AsFieldType(): ApiResponse { + const apiResponse = this as ApiResponse; + + const field = new Field( + apiResponse.data.id, + apiResponse.data.appId, + apiResponse.data.name, + apiResponse.data.type, + apiResponse.data.status, + apiResponse.data.isRequired, + apiResponse.data.isUnique + ); + + return new ApiResponse( + apiResponse.statusCode, + apiResponse.message, + field + ); + } } diff --git a/src/models/Field.ts b/src/models/Field.ts new file mode 100644 index 0000000..a3ac7e9 --- /dev/null +++ b/src/models/Field.ts @@ -0,0 +1,38 @@ +import { FieldStatus } from '../enums/FieldStatus'; +import { FieldType } from '../enums/FieldType'; + +export class Field { + public id: number; + public appId: number; + public name: string; + public type: FieldType; + public status: FieldStatus; + public isRequired: boolean; + public isUnique: boolean; + + public constructor( + id: number, + appId: number, + name: string, + type: string, + status: string, + isRequired: boolean, + isUnique: boolean + ) { + if (FieldType[type] === undefined) { + throw new Error(`The type '${type}' is not a valid FieldType.`); + } + + if (FieldStatus[status] === undefined) { + throw new Error(`The status '${status}' is not a valid FieldStatus.`); + } + + this.id = id; + this.appId = appId; + this.name = name; + this.type = FieldType[type]; + this.status = FieldStatus[status]; + this.isRequired = isRequired; + this.isUnique = isUnique; + } +} diff --git a/src/models/OnspringClient.ts b/src/models/OnspringClient.ts index 5635410..2814b5f 100644 --- a/src/models/OnspringClient.ts +++ b/src/models/OnspringClient.ts @@ -8,6 +8,7 @@ import { PagingRequest } from './PagingRequest'; import { GetPagedAppsResponse } from './GetPagedAppsResponse'; import { App } from './App'; import { CollectionResponse } from './CollectionResponse'; +import { Field } from './Field'; /** * @class OnspringClient - A client that can communicate with the Onspring API. @@ -109,6 +110,17 @@ export class OnspringClient { return apiResponse.AsAppCollectionType(); } + public async getFieldById(fieldId: number): Promise> { + const endpoint = EndpointFactory.getFieldByIdEndpoint(fieldId); + const apiResponse = await this.get(endpoint); + + if (apiResponse.isSuccessful === false) { + return apiResponse; + } + + return apiResponse.AsFieldType(); + } + /** * @method get - Makes a GET request to the specified endpoint. * @param {string} endpoint - The endpoint that will be used to make the request. diff --git a/tests/ApiResponse.spec.ts b/tests/ApiResponse.spec.ts index be92250..f03e102 100644 --- a/tests/ApiResponse.spec.ts +++ b/tests/ApiResponse.spec.ts @@ -3,6 +3,9 @@ import { expect } from 'chai'; import { GetPagedAppsResponse } from '../src/models/GetPagedAppsResponse'; import { App } from '../src/models/App'; import { CollectionResponse } from '../src/models/CollectionResponse'; +import { Field } from '../src/models/Field'; +import { FieldStatus } from '../src/enums/FieldStatus'; +import { FieldType } from '../src/enums/FieldType'; describe('ApiResponse', function () { it('should be defined', function () { @@ -228,4 +231,42 @@ describe('ApiResponse', function () { } }); }); + + describe('AsFieldType', function () { + it('should be defined', function () { + expect(ApiResponse.prototype.AsFieldType).to.not.be.undefined; + }); + + it('should have no parameters', function () { + expect(ApiResponse.prototype.AsFieldType).to.have.lengthOf(0); + }); + + it('should return an ApiResponse when data contain a field', function () { + const mockResponseData = { + id: 1, + appId: 1, + name: 'Text Field', + type: 'Text', + status: 'Enabled', + isRequired: false, + isUnique: false, + }; + + const apiResponse = new ApiResponse(200, 'OK', mockResponseData); + const fieldResponse = apiResponse.AsFieldType(); + + expect(fieldResponse).to.be.instanceOf(ApiResponse); + expect(fieldResponse.data).to.be.instanceOf(Field); + expect(fieldResponse.data).to.not.be.null; + if (fieldResponse.data != null) { + expect(fieldResponse.data.id).to.equal(1); + expect(fieldResponse.data.appId).to.equal(1); + expect(fieldResponse.data.name).to.equal('Text Field'); + expect(fieldResponse.data.type).to.equal(FieldType.Text); + expect(fieldResponse.data.status).to.equal(FieldStatus.Enabled); + expect(fieldResponse.data.isRequired).to.equal(false); + expect(fieldResponse.data.isUnique).to.equal(false); + } + }); + }); }); diff --git a/tests/Field.spec.ts b/tests/Field.spec.ts new file mode 100644 index 0000000..9c1a628 --- /dev/null +++ b/tests/Field.spec.ts @@ -0,0 +1,53 @@ +import { expect } from 'chai'; +import { Field } from '../src/models/Field'; + +describe('Field', function () { + it('should be defined', function () { + expect(Field).to.not.be.undefined; + }); + + it('should have a constructor', function () { + expect(Field).to.have.property('constructor'); + }); + + it('should have 7 parameters', function () { + expect(Field).to.have.lengthOf(7); + }); + + it('should create a new instance of the Field class', function () { + expect( + () => new Field(1, 1, 'Text Field', 'Text', 'Enabled', true, false) + ).to.not.throw(); + }); + + it('should create a new instance of the Field class with the correct properties and values', function () { + const field = new Field(1, 1, 'Text Field', 'Text', 'Enabled', true, false); + + expect(field).to.have.property('id'); + expect(field).to.have.property('appId'); + expect(field).to.have.property('name'); + expect(field).to.have.property('type'); + expect(field).to.have.property('status'); + expect(field).to.have.property('isRequired'); + expect(field).to.have.property('isUnique'); + expect(field.id).to.equal(1); + expect(field.appId).to.equal(1); + expect(field.name).to.equal('Text Field'); + expect(field.type).to.equal('Text'); + expect(field.status).to.equal('Enabled'); + expect(field.isRequired).to.equal(true); + expect(field.isUnique).to.equal(false); + }); + + it('should throw an error when the type parameter is not a valid value', function () { + expect( + () => new Field(1, 1, 'Text Field', 'Invalid', 'Enabled', true, false) + ).to.throw(); + }); + + it('should throw an error when the status parameter is not a valid value', function () { + expect( + () => new Field(1, 1, 'Text Field', 'Text', 'Unabled', true, false) + ).to.throw(); + }); +}); diff --git a/tests/OnspringClient.spec.ts b/tests/OnspringClient.spec.ts index 12d2489..0ed24d1 100644 --- a/tests/OnspringClient.spec.ts +++ b/tests/OnspringClient.spec.ts @@ -6,6 +6,9 @@ import * as sinon from 'sinon'; import { GetPagedAppsResponse } from '../src/models/GetPagedAppsResponse'; import { App } from '../src/models/App'; import { CollectionResponse } from '../src/models/CollectionResponse'; +import { Field } from '../src/models/Field'; +import { FieldStatus } from '../src/enums/FieldStatus'; +import { FieldType } from '../src/enums/FieldType'; describe('OnspringClient', function () { const baseUrl = 'https://api.onspring.dev'; @@ -658,4 +661,174 @@ describe('OnspringClient', function () { expect(result.data).to.be.null; }); }); + + describe('getFieldById', function () { + it('should be defined', function () { + expect(new OnspringClient(baseUrl, apiKey).getFieldById).to.not.be + .undefined; + }); + + it('should be a function', function () { + expect(new OnspringClient(baseUrl, apiKey).getFieldById).to.be.a( + 'function' + ); + }); + + it('should have 1 parameter', function () { + expect(new OnspringClient(baseUrl, apiKey).getFieldById).to.have.lengthOf( + 1 + ); + }); + + it('should return a promise', function () { + expect(new OnspringClient(baseUrl, apiKey).getFieldById(1)).to.be.a( + 'promise' + ); + }); + + it('should return a promise that resolves to an api response of a field 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: { + id: 1, + appId: 1, + name: 'Text Field', + type: 'Text', + status: 'Enabled', + isRequired: false, + isUnique: false, + }, + headers: {}, + config: {} as InternalAxiosRequestConfig, + } as AxiosResponse) + ); + + sinon.stub(client, '_client' as any).value(mockAxiosClient); + + const result = await client.getFieldById(1); + 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'); + if (result.data != null) { + expect(result.data).to.be.instanceOf(Field); + expect(result.data).to.have.property('id', 1); + expect(result.data).to.have.property('appId', 1); + expect(result.data).to.have.property('name', 'Text Field'); + expect(result.data).to.have.property('type', FieldType.Text); + expect(result.data).to.have.property('status', FieldStatus.Enabled); + expect(result.data).to.have.property('isRequired', false); + expect(result.data).to.have.property('isUnique', false); + } + }); + + it('should return a promise that resolves to an api response when request receives a 401 response', 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: 401, + statusText: 'Unauthorized', + headers: {}, + config: {} as InternalAxiosRequestConfig, + } as AxiosResponse) + ); + + sinon.stub(client, '_client' as any).value(mockAxiosClient); + + const result = await client.getFieldById(1); + expect(result).to.be.instanceOf(ApiResponse); + expect(result).to.have.property('statusCode', 401); + expect(result).to.have.property('isSuccessful', false); + expect(result.message).to.be.undefined; + expect(result.data).to.be.null; + }); + + it('should return a promise that resolves to an api response when request receives a 403 response', 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: 403, + statusText: 'Forbidden', + data: { + message: 'Client does not have access to read field: 1', + }, + headers: {}, + config: {} as InternalAxiosRequestConfig, + } as AxiosResponse) + ); + + sinon.stub(client, '_client' as any).value(mockAxiosClient); + + const result = await client.getFieldById(1); + expect(result).to.be.instanceOf(ApiResponse); + expect(result).to.have.property('statusCode', 403); + expect(result).to.have.property('isSuccessful', false); + expect(result).to.have.property( + 'message', + 'Client does not have access to read field: 1' + ); + expect(result.data).to.be.null; + }); + + it('should return a promise that resolves to an api response when request receives a 404 response', 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: 404, + statusText: 'Not Found', + headers: {}, + config: {} as InternalAxiosRequestConfig, + } as AxiosResponse) + ); + + sinon.stub(client, '_client' as any).value(mockAxiosClient); + + const result = await client.getFieldById(1); + expect(result).to.be.instanceOf(ApiResponse); + expect(result).to.have.property('statusCode', 404); + expect(result).to.have.property('isSuccessful', false); + expect(result.message).to.be.undefined; + expect(result.data).to.be.null; + }); + }); });