diff --git a/packages/javascript-api/src/lib/model/line/line-appointment-settings.ts b/packages/javascript-api/src/lib/model/line/line-appointment-settings.ts new file mode 100644 index 00000000..48485e6a --- /dev/null +++ b/packages/javascript-api/src/lib/model/line/line-appointment-settings.ts @@ -0,0 +1,4 @@ +export interface LineAppointmentSettings { + enabled: boolean; + duration: 15 | 30 | 45 | 60 | 90 | 120 | 180; +} diff --git a/packages/javascript-api/src/lib/model/line/line-color.ts b/packages/javascript-api/src/lib/model/line/line-color.ts new file mode 100644 index 00000000..c8584cb2 --- /dev/null +++ b/packages/javascript-api/src/lib/model/line/line-color.ts @@ -0,0 +1,11 @@ +export type LineColor = + | 'VIOLET' + | 'TEAL' + | 'MINT' + | 'CORAL' + | 'YELLOW' + | 'ROSE' + | 'INDIGO' + | 'BLUE' + | 'LAVENDER' + | 'MARSHMALLOW'; diff --git a/packages/javascript-api/src/lib/model/line/line-created-response.ts b/packages/javascript-api/src/lib/model/line/line-created-response.ts new file mode 100644 index 00000000..0a40fa90 --- /dev/null +++ b/packages/javascript-api/src/lib/model/line/line-created-response.ts @@ -0,0 +1,3 @@ +export interface LineCreatedResponse { + id: string; +} diff --git a/packages/javascript-api/src/lib/model/line/line-creation-request.ts b/packages/javascript-api/src/lib/model/line/line-creation-request.ts new file mode 100644 index 00000000..b4dcd4b9 --- /dev/null +++ b/packages/javascript-api/src/lib/model/line/line-creation-request.ts @@ -0,0 +1,11 @@ +import { LineAppointmentSettings } from './line-appointment-settings.js'; +import { LineColor } from './line-color.js'; +import { LineTranslation } from './line-translation.js'; + +export interface LineCreationRequest { + name: string; + color: LineColor; + disabled?: boolean; + translations?: LineTranslation[]; + appointmentSettings?: LineAppointmentSettings; +} diff --git a/packages/javascript-api/src/lib/model/line/line-translation.ts b/packages/javascript-api/src/lib/model/line/line-translation.ts new file mode 100644 index 00000000..05f3bd61 --- /dev/null +++ b/packages/javascript-api/src/lib/model/line/line-translation.ts @@ -0,0 +1,4 @@ +export interface LineTranslation { + languageCode: string; + name?: string; +} diff --git a/packages/javascript-api/src/lib/services/line/line.service.spec.ts b/packages/javascript-api/src/lib/services/line/line.service.spec.ts index 39de9e4e..a817167f 100644 --- a/packages/javascript-api/src/lib/services/line/line.service.spec.ts +++ b/packages/javascript-api/src/lib/services/line/line.service.spec.ts @@ -1,5 +1,7 @@ import * as sinon from 'sinon'; import { Line } from '../../model/line'; +import { LineCreationRequest } from '../../model/line/line-creation-request'; +import { ResponseValidationError } from '../../model/errors/response-validation-error'; import { Qminder } from '../../qminder'; import { LineService } from './line.service'; @@ -72,6 +74,59 @@ describe('Line service', function () { }); }); + describe('create()', function () { + const SUCCESSFUL_RESPONSE = { id: '12345' }; + + it('sends the request to the correct URL with JSON body and version headers', async function () { + requestStub.resolves(SUCCESSFUL_RESPONSE); + const request: LineCreationRequest = { + name: 'Priority Service', + color: 'TEAL', + }; + const result = await LineService.create(LOCATION_ID, request); + expect(requestStub.firstCall.args).toEqual([ + `locations/${LOCATION_ID}/lines`, + { + method: 'POST', + body: JSON.stringify(request), + headers: { 'X-Qminder-API-Version': '2020-09-01' }, + }, + ]); + expect(result).toEqual(SUCCESSFUL_RESPONSE); + }); + + it('sends optional fields when provided', async function () { + requestStub.resolves(SUCCESSFUL_RESPONSE); + const request: LineCreationRequest = { + name: 'Priority Service', + color: 'VIOLET', + disabled: true, + translations: [{ languageCode: 'fr', name: 'Service Prioritaire' }], + appointmentSettings: { enabled: true, duration: 30 }, + }; + await LineService.create(LOCATION_ID, request); + expect(requestStub.firstCall.args).toEqual([ + `locations/${LOCATION_ID}/lines`, + { + method: 'POST', + body: JSON.stringify(request), + headers: { 'X-Qminder-API-Version': '2020-09-01' }, + }, + ]); + }); + + it('throws when response does not contain id', async function () { + requestStub.resolves({}); + const request: LineCreationRequest = { + name: 'Priority Service', + color: 'TEAL', + }; + await expect(LineService.create(LOCATION_ID, request)).rejects.toThrow( + new ResponseValidationError('Response does not contain "id"'), + ); + }); + }); + describe('update()', function () { beforeEach(function (done) { requestStub.withArgs('v1/lines/71490').resolves({}); diff --git a/packages/javascript-api/src/lib/services/line/line.service.ts b/packages/javascript-api/src/lib/services/line/line.service.ts index 75178bc9..b6ca6f05 100644 --- a/packages/javascript-api/src/lib/services/line/line.service.ts +++ b/packages/javascript-api/src/lib/services/line/line.service.ts @@ -58,20 +58,24 @@ export const LineService = { details, /** - * Create a new Line and return its details. + * Create a new Line and return its ID. * * Calls the following HTTP API: `POST /locations//lines` * * For example: * * ```javascript - * const line: Line = await Qminder.Line.create(950, { name: 'Priority Service' }); - * console.log(line.id); // 1425 + * const response = await Qminder.Line.create(950, { + * name: 'Priority Service', + * color: 'TEAL', + * translations: [{ languageCode: 'fr', name: 'Service Prioritaire' }], + * appointmentSettings: { enabled: true, duration: 30 }, + * }); + * console.log(response.id); // "1425" * ``` * @param location the location to add the line under - * @param line the parameters of the new line - must include the line name - * @returns a Promise that resolves to a new Line object, created according - * to the parameters. + * @param line the parameters of the new line - must include name and color + * @returns a Promise that resolves to a LineCreatedResponse containing the new line's ID. */ create, diff --git a/packages/javascript-api/src/lib/services/line/line.ts b/packages/javascript-api/src/lib/services/line/line.ts index b167fdfb..27944c35 100644 --- a/packages/javascript-api/src/lib/services/line/line.ts +++ b/packages/javascript-api/src/lib/services/line/line.ts @@ -1,9 +1,12 @@ import { Line } from '../../model/line.js'; +import { LineCreatedResponse } from '../../model/line/line-created-response.js'; +import { LineCreationRequest } from '../../model/line/line-creation-request.js'; import { Location } from '../../model/location.js'; import { extractId, IdOrObject } from '../../util/id-or-object.js'; import { ApiBase, SuccessResponse } from '../api-base/api-base.js'; +import { V2_HEADERS } from '../v2-headers.js'; +import { ResponseValidationError } from '../../model/errors/response-validation-error.js'; -type LineCreateParameters = Partial> & Pick; type LineUpdateParameters = Pick & Partial>; @@ -25,10 +28,10 @@ export function details(line: IdOrObject): Promise { return ApiBase.request(`v1/lines/${lineId}/`); } -export function create( +export async function create( location: IdOrObject, - line: LineCreateParameters, -): Promise { + line: LineCreationRequest, +): Promise { const locationId = extractId(location); if (!locationId || typeof locationId !== 'string') { throw new Error('Location ID invalid or missing.'); @@ -39,10 +42,24 @@ export function create( if (!line.name || typeof line.name !== 'string') { throw new Error('Cannot create a line without a line name.'); } - return ApiBase.request(`v1/locations/${locationId}/lines`, { - body: line, - method: 'POST', - }) as Promise; + if (!line.color || typeof line.color !== 'string') { + throw new Error('Cannot create a line without a color.'); + } + + const result: LineCreatedResponse = await ApiBase.request( + `locations/${locationId}/lines`, + { + method: 'POST', + body: JSON.stringify(line), + headers: V2_HEADERS, + }, + ); + + if (!result.id) { + throw new ResponseValidationError('Response does not contain "id"'); + } + + return result; } export function update(line: LineUpdateParameters): Promise { diff --git a/packages/javascript-api/src/public-api/model.ts b/packages/javascript-api/src/public-api/model.ts index dca6a840..45a378a3 100644 --- a/packages/javascript-api/src/public-api/model.ts +++ b/packages/javascript-api/src/public-api/model.ts @@ -5,6 +5,11 @@ export { Desk } from '../lib/model/desk.js'; export { Device } from '../lib/model/device.js'; export { Id } from '../lib/model/id.js'; export { Line } from '../lib/model/line.js'; +export { LineColor } from '../lib/model/line/line-color.js'; +export { LineTranslation } from '../lib/model/line/line-translation.js'; +export { LineAppointmentSettings } from '../lib/model/line/line-appointment-settings.js'; +export { LineCreationRequest } from '../lib/model/line/line-creation-request.js'; +export { LineCreatedResponse } from '../lib/model/line/line-created-response.js'; export { Location } from '../lib/model/location.js'; export { TicketExtra } from '../lib/model/ticket/ticket-extra.js'; export { TicketLabel } from '../lib/model/ticket/ticket-label.js';