Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { InputFieldBase } from './input-field-base.js';
import { InputFieldTranslation } from './input-field-translation.js';

export interface DateFieldCreationRequest extends InputFieldBase {
type: 'DATE';
title: string;
visitorFacingTitle?: string;
translations?: InputFieldTranslation[];
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { InputFieldBase } from './input-field-base.js';

export interface EmailFieldCreationRequest extends InputFieldBase {
type: 'EMAIL';
isRequiredInAppointments: boolean;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { InputFieldBase } from './input-field-base.js';

export interface FirstNameFieldCreationRequest extends InputFieldBase {
type: 'FIRST_NAME';
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { LineRef } from './line-ref.js';
import { LocationRef } from './location-ref.js';
import { UUID } from '../uuid.js';

export interface InputFieldBase {
id: UUID;
location: LocationRef;
isMandatoryBeforeAdded: boolean;
isMandatoryBeforeServed: boolean;
isMandatoryInRemoteSignIn: boolean;
isVisibleInWaitingDrawer: boolean;
isVisibleInServingDrawer: boolean;
visibleForLines: LineRef[];
showInRemoteSignIn: boolean;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { DateFieldCreationRequest } from './date-field-creation-request.js';
import { EmailFieldCreationRequest } from './email-field-creation-request.js';
import { FirstNameFieldCreationRequest } from './first-name-field-creation-request.js';
import { LastNameFieldCreationRequest } from './last-name-field-creation-request.js';
import { NumericFieldCreationRequest } from './numeric-field-creation-request.js';
import { PhoneNumberFieldCreationRequest } from './phone-number-field-creation-request.js';
import { SelectFieldCreationRequest } from './select-field-creation-request.js';
import { TextFieldCreationRequest } from './text-field-creation-request.js';
import { UrlFieldCreationRequest } from './url-field-creation-request.js';

/**
* A discriminated union of all input field creation request types.
* The `type` field determines which properties are available.
*/
export type InputFieldCreationRequest =
| TextFieldCreationRequest
| SelectFieldCreationRequest
| EmailFieldCreationRequest
| PhoneNumberFieldCreationRequest
| FirstNameFieldCreationRequest
| LastNameFieldCreationRequest
| UrlFieldCreationRequest
| DateFieldCreationRequest
| NumericFieldCreationRequest;
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export interface InputFieldTranslation {
languageCode: string;
title?: string;
visitorFacingTitle?: string;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { InputFieldBase } from './input-field-base.js';

export interface LastNameFieldCreationRequest extends InputFieldBase {
type: 'LAST_NAME';
isRequiredInAppointments?: boolean;
}
3 changes: 3 additions & 0 deletions packages/javascript-api/src/lib/model/input-field/line-ref.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export interface LineRef {
id: number;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export interface LocationRef {
id: number;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export interface NumericFieldConstraints {
min?: number;
max?: number;
scale: number;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { InputFieldBase } from './input-field-base.js';
import { InputFieldTranslation } from './input-field-translation.js';
import { NumericFieldConstraints } from './numeric-field-constraints.js';

export interface NumericFieldCreationRequest extends InputFieldBase {
type: 'NUMERIC';
title: string;
visitorFacingTitle?: string;
translations?: InputFieldTranslation[];
constraints?: NumericFieldConstraints;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { InputFieldBase } from './input-field-base.js';

export interface PhoneNumberFieldCreationRequest extends InputFieldBase {
type: 'PHONE_NUMBER';
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { InputFieldBase } from './input-field-base.js';
import { InputFieldTranslation } from './input-field-translation.js';
import { SelectOption } from './select-option.js';

export interface SelectFieldCreationRequest extends InputFieldBase {
type: 'SELECT';
title: string;
visitorFacingTitle?: string;
multiSelect: boolean;
options: SelectOption[];
translations?: InputFieldTranslation[];
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export interface SelectOptionTranslation {
languageCode: string;
title?: string;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { UUID } from '../uuid.js';
import { SelectOptionTranslation } from './select-option-translation.js';

export interface SelectOption {
id: UUID;
title: string;
color?: string;
translations?: SelectOptionTranslation[];
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { InputFieldBase } from './input-field-base.js';
import { InputFieldTranslation } from './input-field-translation.js';

export interface TextFieldCreationRequest extends InputFieldBase {
type: 'TEXT';
title: string;
visitorFacingTitle?: string;
translations?: InputFieldTranslation[];
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { InputFieldBase } from './input-field-base.js';
import { InputFieldTranslation } from './input-field-translation.js';

export interface UrlFieldCreationRequest extends InputFieldBase {
type: 'URL';
title: string;
translations?: InputFieldTranslation[];
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import * as sinon from 'sinon';
import { Desk } from '../../model/desk';
import { InputFieldCreationRequest } from '../../model/input-field/input-field-creation-request';
import { FirstNameFieldCreationRequest } from '../../model/input-field/first-name-field-creation-request';
import { NumericFieldCreationRequest } from '../../model/input-field/numeric-field-creation-request';
import { SelectFieldCreationRequest } from '../../model/input-field/select-field-creation-request';
import { Qminder } from '../../qminder';
import { LocationService } from './location.service';

Expand Down Expand Up @@ -225,6 +229,133 @@ describe('Location service', function () {
});
});

describe('createInputField()', function () {
beforeEach(function () {
requestStub.withArgs('input-fields').resolves({});
});

it('sends a TEXT field with correct URL, method, body and headers', async function () {
const textField: InputFieldCreationRequest = {
type: 'TEXT',
id: 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
location: { id: LOCATION_ID },
title: 'Email address',
visitorFacingTitle: 'Your email',
isMandatoryBeforeAdded: false,
isMandatoryBeforeServed: false,
isMandatoryInRemoteSignIn: false,
isVisibleInWaitingDrawer: true,
isVisibleInServingDrawer: true,
visibleForLines: [{ id: 1 }, { id: 2 }],
showInRemoteSignIn: false,
translations: [
{
languageCode: 'et',
title: 'E-posti aadress',
visitorFacingTitle: 'Sinu e-post',
},
],
};

await LocationService.createInputField(textField);
expect(
requestStub.calledWith('input-fields', {
method: 'POST',
body: JSON.stringify(textField),
headers: { 'X-Qminder-API-Version': '2020-09-01' },
}),
).toBeTruthy();
});

it('sends a SELECT field with options', async function () {
const selectField: SelectFieldCreationRequest = {
type: 'SELECT',
id: 'b2c3d4e5-f6a7-8901-bcde-f12345678901',
location: { id: LOCATION_ID },
title: 'Service type',
multiSelect: false,
options: [
{
id: 'c3d4e5f6-a7b8-9012-cdef-123456789012',
title: 'Documents',
color: '#FF0000',
translations: [{ languageCode: 'et', title: 'Dokumendid' }],
},
{
id: 'd4e5f6a7-b8c9-0123-defa-234567890123',
title: 'Consultation',
},
],
isMandatoryBeforeAdded: true,
isMandatoryBeforeServed: false,
isMandatoryInRemoteSignIn: false,
isVisibleInWaitingDrawer: true,
isVisibleInServingDrawer: true,
visibleForLines: [],
showInRemoteSignIn: false,
};

await LocationService.createInputField(selectField);
expect(
requestStub.calledWith('input-fields', {
method: 'POST',
body: JSON.stringify(selectField),
headers: { 'X-Qminder-API-Version': '2020-09-01' },
}),
).toBeTruthy();
});

it('sends a NUMERIC field with constraints', async function () {
const numericField: NumericFieldCreationRequest = {
type: 'NUMERIC',
id: 'e5f6a7b8-c9d0-1234-efab-345678901234',
location: { id: LOCATION_ID },
title: 'Amount',
constraints: { min: 0, max: 1000, scale: 2 },
isMandatoryBeforeAdded: false,
isMandatoryBeforeServed: true,
isMandatoryInRemoteSignIn: false,
isVisibleInWaitingDrawer: false,
isVisibleInServingDrawer: true,
visibleForLines: [],
showInRemoteSignIn: false,
};

await LocationService.createInputField(numericField);
expect(
requestStub.calledWith('input-fields', {
method: 'POST',
body: JSON.stringify(numericField),
headers: { 'X-Qminder-API-Version': '2020-09-01' },
}),
).toBeTruthy();
});

it('sends a FIRST_NAME field with only base properties', async function () {
const firstNameField: FirstNameFieldCreationRequest = {
type: 'FIRST_NAME',
id: 'f6a7b8c9-d0e1-2345-faba-456789012345',
location: { id: LOCATION_ID },
isMandatoryBeforeAdded: false,
isMandatoryBeforeServed: false,
isMandatoryInRemoteSignIn: false,
isVisibleInWaitingDrawer: true,
isVisibleInServingDrawer: true,
visibleForLines: [],
showInRemoteSignIn: false,
};

await LocationService.createInputField(firstNameField);
expect(
requestStub.calledWith('input-fields', {
method: 'POST',
body: JSON.stringify(firstNameField),
headers: { 'X-Qminder-API-Version': '2020-09-01' },
}),
).toBeTruthy();
});
});

afterEach(function () {
requestStub.restore();
});
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {
createInputField,
details,
getDesks,
list,
Expand Down Expand Up @@ -94,4 +95,11 @@ export const LocationService = {
* Calls the following HTTP API: `PUT /locations/<ID>/opening-hours/exceptions`
*/
setOpeningHoursExceptions,

/**
* Create a new input field for a location.
*
* Calls the following HTTP API: `POST /input-fields`
*/
createInputField,
};
14 changes: 12 additions & 2 deletions packages/javascript-api/src/lib/services/location/location.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { Desk } from '../../model/desk.js';
import { InputFieldCreationRequest } from '../../model/input-field/input-field-creation-request.js';
import { Location } from '../../model/location.js';
import { OpeningHours } from '../../model/opening-hours.js';
import { OpeningHoursException } from '../../model/opening-hours-exception.js';
import { extractId, IdOrObject } from '../../util/id-or-object.js';
import { ApiBase } from '../api-base/api-base.js';

const V2_HEADERS = { 'X-Qminder-API-Version': '2020-09-01' } as const;
import { V2_HEADERS } from '../v2-headers.js';

export function list(): Promise<Location[]> {
return ApiBase.request('v1/locations/').then(
Expand Down Expand Up @@ -55,3 +55,13 @@ export async function setOpeningHoursExceptions(
headers: V2_HEADERS,
});
}

export async function createInputField(
inputField: InputFieldCreationRequest,
): Promise<void> {
await ApiBase.request('input-fields', {
method: 'POST',
body: JSON.stringify(inputField),
headers: V2_HEADERS,
});
}
9 changes: 3 additions & 6 deletions packages/javascript-api/src/lib/services/ticket/ticket.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
extractIdToNumber,
} from '../../util/id-or-object.js';
import { ApiBase } from '../api-base/api-base.js';
import { V2_HEADERS } from '../v2-headers.js';
import { ResponseValidationError } from '../../model/errors/response-validation-error.js';
import { ExternalData } from '../../model/ticket/external-data.js';

Expand Down Expand Up @@ -329,9 +330,7 @@ export async function create(
const result: TicketCreatedResponse = await ApiBase.request('tickets', {
method: 'POST',
body,
headers: {
'X-Qminder-API-Version': '2020-09-01',
},
headers: V2_HEADERS,
});
if (!result.id) {
throw new ResponseValidationError('Response does not contain "id"');
Expand Down Expand Up @@ -360,9 +359,7 @@ export async function edit(
await ApiBase.request(`tickets/${ticketId}`, {
method: 'PATCH',
body,
headers: {
'X-Qminder-API-Version': '2020-09-01',
},
headers: V2_HEADERS,
});
}

Expand Down
1 change: 1 addition & 0 deletions packages/javascript-api/src/lib/services/v2-headers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const V2_HEADERS = { 'X-Qminder-API-Version': '2020-09-01' } as const;
16 changes: 16 additions & 0 deletions packages/javascript-api/src/public-api/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,19 @@ export {
export { TicketLabelRequest } from '../lib/model/ticket/ticket-label-request.js';
export { TicketType } from '../lib/model/ticket/ticket-type.js';
export { UUID } from '../lib/model/uuid.js';
export { InputFieldCreationRequest } from '../lib/model/input-field/input-field-creation-request.js';
export { LocationRef } from '../lib/model/input-field/location-ref.js';
export { LineRef } from '../lib/model/input-field/line-ref.js';
export { InputFieldTranslation } from '../lib/model/input-field/input-field-translation.js';
export { SelectOption } from '../lib/model/input-field/select-option.js';
export { SelectOptionTranslation } from '../lib/model/input-field/select-option-translation.js';
export { NumericFieldConstraints } from '../lib/model/input-field/numeric-field-constraints.js';
export { TextFieldCreationRequest } from '../lib/model/input-field/text-field-creation-request.js';
export { SelectFieldCreationRequest } from '../lib/model/input-field/select-field-creation-request.js';
export { EmailFieldCreationRequest } from '../lib/model/input-field/email-field-creation-request.js';
export { PhoneNumberFieldCreationRequest } from '../lib/model/input-field/phone-number-field-creation-request.js';
export { FirstNameFieldCreationRequest } from '../lib/model/input-field/first-name-field-creation-request.js';
export { LastNameFieldCreationRequest } from '../lib/model/input-field/last-name-field-creation-request.js';
export { UrlFieldCreationRequest } from '../lib/model/input-field/url-field-creation-request.js';
export { DateFieldCreationRequest } from '../lib/model/input-field/date-field-creation-request.js';
export { NumericFieldCreationRequest } from '../lib/model/input-field/numeric-field-creation-request.js';
Loading