feat: implement addOrUpdateListItem method

This commit is contained in:
Stevan Freeborn
2023-02-07 11:52:46 -06:00
parent a68ea4f63e
commit 190c829dc3
8 changed files with 391 additions and 0 deletions
+13
View File
@@ -10,6 +10,7 @@ import { FormulaField } from './FormulaField';
import { GetPagedAppsResponse } from './GetPagedAppsResponse'; import { GetPagedAppsResponse } from './GetPagedAppsResponse';
import { GetPagedFieldsResponse } from './GetPagedFieldsResponse'; import { GetPagedFieldsResponse } from './GetPagedFieldsResponse';
import { ListField } from './ListField'; import { ListField } from './ListField';
import { ListItemResponse } from './ListItemResponse';
import { ListValue } from './ListValue'; import { ListValue } from './ListValue';
import { ReferenceField } from './ReferenceField'; import { ReferenceField } from './ReferenceField';
@@ -258,6 +259,18 @@ export class ApiResponse<T> {
); );
} }
public asListItemResponseType(): ApiResponse<ListItemResponse> {
const apiResponse = this as ApiResponse<any>;
const listItemResponse = new ListItemResponse(apiResponse.data.id);
return new ApiResponse<ListItemResponse>(
apiResponse.statusCode,
apiResponse.message,
listItemResponse
);
}
/** /**
* @method asFileCollectionType - Converts the field item to the appropriate field object based upon the field item's type. * @method asFileCollectionType - Converts the field item to the appropriate field object based upon the field item's type.
* @param fieldItem - The field item to convert. * @param fieldItem - The field item to convert.
+52
View File
@@ -0,0 +1,52 @@
/**
* @class ListItemRequest - Represents a list item request
*/
export class ListItemRequest {
/**
* @property {number} listId - The id for the list that the item belongs to.
*/
public listId: number;
/**
* @property {string} id - The id for the list item.
*/
public id: string;
/**
* @property {string} name - The name of the list item.
*/
public name: string;
/**
* @property {number} numericValue - The numeric value of the list item.
*/
public numericValue: number;
/**
* @property {string} color - The color of the list item.
*/
public color: string;
/**
* @constructor - Creates a new instance of the ListItemRequest class.
* @param {number} listId - The id for the list that the item belongs to.
* @param {string} id - The id for the list item.
* @param {string} name - The name of the list item.
* @param {number} numericValue - The numeric value of the list item.
* @param {string} color - The color of the list item.
* @returns {ListItemRequest} - A new instance of the ListItemRequest class.
*/
constructor(
listId: number,
id: string,
name: string,
numericValue: number,
color: string
) {
this.listId = listId;
this.id = id;
this.name = name;
this.numericValue = numericValue;
this.color = color;
}
}
+10
View File
@@ -0,0 +1,10 @@
import { CreatedWithIdResponse } from './CreatedWithIdResponse';
/**
* @class ListItemResponse - Represents a respons when a list item is created or updated.
*/
export class ListItemResponse extends CreatedWithIdResponse {
constructor(id: number) {
super(id);
}
}
+41
View File
@@ -14,6 +14,8 @@ import { type SaveFileRequest } from './SaveFileRequest';
import { type CreatedWithIdResponse } from './CreatedWithIdResponse'; import { type CreatedWithIdResponse } from './CreatedWithIdResponse';
import { type FileInfo } from './FileInfo'; import { type FileInfo } from './FileInfo';
import { type File } from './File'; import { type File } from './File';
import { type ListItemResponse } from './ListItemResponse';
import { type ListItemRequest } from './ListItemRequest';
/** /**
* @class OnspringClient - A client that can communicate with the Onspring API. * @class OnspringClient - A client that can communicate with the Onspring API.
@@ -269,6 +271,22 @@ export class OnspringClient {
return apiResponse; return apiResponse;
} }
public async addOrUpdateListItem(
listItemRequest: ListItemRequest
): Promise<ApiResponse<ListItemResponse>> {
const { listId, ...data } = listItemRequest;
const endpoint = EndpointFactory.getAddOrUpdateListItemEndpoint(listId);
const apiResponse = await this.put<any>(endpoint, data);
if (apiResponse.isSuccessful === false) {
return apiResponse;
}
return apiResponse.asListItemResponseType();
}
/** /**
* @method get - Makes a GET request to the specified endpoint. * @method get - Makes a GET request to the specified endpoint.
* @param {string} endpoint - The endpoint that will be used to make the request. * @param {string} endpoint - The endpoint that will be used to make the request.
@@ -301,6 +319,29 @@ export class OnspringClient {
return apiResponse; return apiResponse;
} }
/**
* @method put - Makes a PUT request to the specified endpoint.
* @param {string} endpoint - The endpoint that will be used to make the request.
* @param {any} data - The data that will be sent with the request.
* @param {AxiosRequestConfig} config - The configuration that will be used to make the request.
* @returns {Promise<ApiResponse<T>>} - A promise that resolves to an ApiResponse of type T.
*/
private async put<T>(
endpoint: string,
data: any,
config: AxiosRequestConfig = {}
): Promise<ApiResponse<T>> {
const response = await this._client.put(endpoint, data, config);
const apiResponse = ApiResponseFactory.getApiResponse<T>(response);
return apiResponse;
}
/**
* @method delete - Makes a DELETE request to the specified endpoint.
* @param {string} endpoint - The endpoint that will be used to make the request.
* @param {AxiosRequestConfig} config - The configuration that will be used to make the request.
* @returns {Promise<ApiResponse<T>>} - A promise that resolves to an ApiResponse of type T.
*/
private async delete<T>( private async delete<T>(
endpoint: string, endpoint: string,
config: AxiosRequestConfig = {} config: AxiosRequestConfig = {}
+26
View File
@@ -18,6 +18,7 @@ import { File } from '../src/models/File';
import fs from 'fs'; import fs from 'fs';
import { type AxiosResponse } from 'axios'; import { type AxiosResponse } from 'axios';
import path from 'path'; import path from 'path';
import { ListItemResponse } from '../src/models/ListItemResponse';
describe('ApiResponse', function () { describe('ApiResponse', function () {
it('should be defined', function () { it('should be defined', function () {
@@ -916,4 +917,29 @@ describe('ApiResponse', function () {
expect(fileResponse.data).to.have.property('contentLength', 0); expect(fileResponse.data).to.have.property('contentLength', 0);
}); });
}); });
describe('asListItemResponseType', function () {
it('should be defined', function () {
expect(ApiResponse.prototype.asListItemResponseType).to.not.be.undefined;
});
it('should be a function', function () {
expect(ApiResponse.prototype.asListItemResponseType).to.be.a('function');
});
it('should have no parameters', function () {
expect(ApiResponse.prototype.asListItemResponseType.length).to.equal(0);
});
it('should return an ApiResponse<ListItemResponse>', function () {
const apiResponse = new ApiResponse(200, 'OK', {
id: '3fa85f64-5717-4562-b3fc-2c963f66afa6',
});
const listItemResponse = apiResponse.asListItemResponseType();
expect(listItemResponse).to.be.an.instanceof(ApiResponse);
expect(listItemResponse.data).to.be.an.instanceof(ListItemResponse);
expect(listItemResponse.data).to.have.property(
'id',
'3fa85f64-5717-4562-b3fc-2c963f66afa6'
);
});
});
}); });
+33
View File
@@ -0,0 +1,33 @@
import { ListItemRequest } from '../src/models/ListItemRequest';
import { expect } from 'chai';
describe('ListItemRequest', function () {
it('should be defined', function () {
expect(ListItemRequest).to.not.be.undefined;
});
it('should have a constructor with 5 parameters', function () {
expect(ListItemRequest).to.have.property('constructor');
expect(ListItemRequest).to.have.lengthOf(5);
});
it('should have a constructor that takes a listId, id, name, numericValue, and color and sets the listId, id, name, numericValue, and color properties', function () {
const listId = 1;
const id = '1';
const name = 'name';
const numericValue = 1;
const color = 'color';
const listItemRequest = new ListItemRequest(
listId,
id,
name,
numericValue,
color
);
expect(listItemRequest).to.have.property('listId', listId);
expect(listItemRequest).to.have.property('id', id);
expect(listItemRequest).to.have.property('name', name);
expect(listItemRequest).to.have.property('numericValue', numericValue);
expect(listItemRequest).to.have.property('color', color);
});
});
+19
View File
@@ -0,0 +1,19 @@
import { ListItemResponse } from '../src/models/ListItemResponse';
import { expect } from 'chai';
describe('ListItemResponse', function () {
it('should be defined', function () {
expect(ListItemResponse).to.not.be.undefined;
});
it('should have a constructor with 1 parameter', function () {
expect(ListItemResponse).to.have.property('constructor');
expect(ListItemResponse.constructor).to.have.lengthOf(1);
});
it('should have a constructor that takes an id and sets the id property', function () {
const id = 1;
const listItemResponse = new ListItemResponse(id);
expect(listItemResponse).to.have.property('id', id);
});
});
+197
View File
@@ -20,6 +20,8 @@ import { FileInfo } from '../src/models/FileInfo';
import { File } from '../src/models/File'; import { File } from '../src/models/File';
import fs from 'fs'; import fs from 'fs';
import path from 'path'; import path from 'path';
import { ListItemRequest } from '../src/models/ListItemRequest';
import { ListItemResponse } from '../src/models/ListItemResponse';
describe('OnspringClient', function () { describe('OnspringClient', function () {
const baseUrl = 'https://api.onspring.dev'; const baseUrl = 'https://api.onspring.dev';
@@ -2108,4 +2110,199 @@ describe('OnspringClient', function () {
expect(result.data).to.be.null; expect(result.data).to.be.null;
}); });
}); });
describe('addOrUpdateListItem', function () {
it('should be defined', function () {
expect(OnspringClient.prototype.addOrUpdateListItem).to.not.be.undefined;
});
it('should be a function', function () {
expect(OnspringClient.prototype.addOrUpdateListItem).to.be.a('function');
});
it('should return a promise', function () {
expect(
new OnspringClient(baseUrl, apiKey).addOrUpdateListItem(
new ListItemRequest(1, 'id', 'value', 1, 'color')
)
).to.be.instanceOf(Promise);
});
it('should return a promise that resolves to an api response when request is successful at updating a list item', 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, 'put').returns(
Promise.resolve({
status: 200,
statusText: 'OK',
data: { id: '3fa85f64-5717-4562-b3fc-2c963f66afa6' },
headers: {},
config: {} as InternalAxiosRequestConfig,
} as AxiosResponse)
);
sinon.stub(client, '_client' as any).value(mockAxiosClient);
const result = await client.addOrUpdateListItem(
new ListItemRequest(1, 'id', 'value', 1, 'color')
);
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.data).to.be.instanceOf(ListItemResponse);
expect(result.data).to.have.property(
'id',
'3fa85f64-5717-4562-b3fc-2c963f66afa6'
);
});
it('should return a promise that resolves to an api response when request is successful at adding a list item', 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, 'put').returns(
Promise.resolve({
status: 201,
statusText: 'OK',
data: { id: '3fa85f64-5717-4562-b3fc-2c963f66afa6' },
headers: {},
config: {} as InternalAxiosRequestConfig,
} as AxiosResponse)
);
sinon.stub(client, '_client' as any).value(mockAxiosClient);
const result = await client.addOrUpdateListItem(
new ListItemRequest(1, 'id', 'value', 1, 'color')
);
expect(result).to.be.instanceOf(ApiResponse);
expect(result).to.have.property('statusCode', 201);
expect(result).to.have.property('isSuccessful', true);
expect(result).to.have.property('message', '');
expect(result.data).to.be.instanceOf(ListItemResponse);
expect(result.data).to.have.property(
'id',
'3fa85f64-5717-4562-b3fc-2c963f66afa6'
);
});
});
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, 'put').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.addOrUpdateListItem(
new ListItemRequest(1, 'id', 'value', 1, 'color')
);
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, 'put').returns(
Promise.resolve({
status: 403,
statusText: 'Forbidden',
data: { message: 'Forbidden' },
headers: {},
config: {} as InternalAxiosRequestConfig,
} as AxiosResponse)
);
sinon.stub(client, '_client' as any).value(mockAxiosClient);
const result = await client.addOrUpdateListItem(
new ListItemRequest(1, 'id', 'value', 1, 'color')
);
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', 'Forbidden');
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, 'put').returns(
Promise.resolve({
status: 404,
statusText: 'Not Found',
data: { message: 'Not Found' },
headers: {},
config: {} as InternalAxiosRequestConfig,
} as AxiosResponse)
);
sinon.stub(client, '_client' as any).value(mockAxiosClient);
const result = await client.addOrUpdateListItem(
new ListItemRequest(1, 'id', 'value', 1, 'color')
);
expect(result).to.be.instanceOf(ApiResponse);
expect(result).to.have.property('statusCode', 404);
expect(result).to.have.property('isSuccessful', false);
expect(result).to.have.property('message', 'Not Found');
expect(result.data).to.be.null;
});
}); });