diff --git a/API.md b/API.md index 1f646f9..1fef5dc 100644 --- a/API.md +++ b/API.md @@ -405,10 +405,10 @@ The store is exposed as a `LibDashStore` instance (see `src/index.js`). Each met - `delete(googleAuthId: number, id: number, tx?: object): Promise` - `listByManifestId(googleAuthId: number, manifestId: number, tx?: object): Promise>` - [printeragent](API.printeragent.md) - - `create(googleAuthId: number, input: {name: string, pairingpin: string}, tx?: object): Promise<{id: number, companyId: number, name: string, token: string|null, pairingpin: string|null, pairedAt: string|null, clientserial: string|null, clientmodel: string|null, clientstate: string|null, agentversion: string|null, lastseenAt: string|null, updatedAt: string}>` + - `create(googleAuthId: number, input: {name: string, pairingpin: string, sort?: number}, tx?: object): Promise<{id: number, companyId: number, name: string, token: string|null, pairingpin: string|null, pairedAt: string|null, clientserial: string|null, clientmodel: string|null, clientstate: string|null, agentversion: string|null, lastseenAt: string|null, updatedAt: string, sort: number}>` - `delete(googleAuthId: number, id: number, tx?: object): Promise` - - `list(googleAuthId: number, tx?: object): Promise>` - - `update(googleAuthId: number, input: {id: number, name: string}, tx?: object): Promise<{id: number, companyId: number, name: string, token: string|null, pairingpin: string|null, pairedAt: string|null, clientserial: string|null, clientmodel: string|null, clientstate: string|null, agentversion: string|null, lastseenAt: string|null, updatedAt: string}>` + - `list(googleAuthId: number, tx?: object): Promise>` + - `update(googleAuthId: number, input: {id: number, name?: string, sort?: number}, tx?: object): Promise<{id: number, companyId: number, name: string, token: string|null, pairingpin: string|null, pairedAt: string|null, clientserial: string|null, clientmodel: string|null, clientstate: string|null, agentversion: string|null, lastseenAt: string|null, updatedAt: string, sort: number}>` - [retoure](API.retoure.md) - `create(googleAuthId: number, body: {number?: string, date: string, entityId?: number, externalNumber?: string, lines?: Array}, tx?: object): Promise` - `createShipment(googleAuthId: number, retoureId: number, input: object, tx?: object): Promise` diff --git a/API.printeragent.md b/API.printeragent.md index 95386c3..0fe6c58 100644 --- a/API.printeragent.md +++ b/API.printeragent.md @@ -16,15 +16,16 @@ Creates an unpaired printer agent for the user's selected company. The agent reg | `input` | `object` | The printer agent input. | | `input.name` | `string` | The display name. | | `input.pairingpin` | `string` | The pairing pin the agent uses to register. | +| `[input.sort]` | `number` | Display order (integer, ascending). Default 0. | | `[tx]` | `object` | Optional transaction to run within. | **Returns** -`Promise<{id: number, companyId: number, name: string, token: string|null, pairingpin: string|null, pairedAt: string|null, clientserial: string|null, clientmodel: string|null, clientstate: string|null, agentversion: string|null, lastseenAt: string|null, updatedAt: string}>` — The created printer agent. +`Promise<{id: number, companyId: number, name: string, token: string|null, pairingpin: string|null, pairedAt: string|null, clientserial: string|null, clientmodel: string|null, clientstate: string|null, agentversion: string|null, lastseenAt: string|null, updatedAt: string, sort: number}>` — The created printer agent. **Throws** -`Error` — 'Invalid printeragent' if name or pairingpin is missing/blank. +`Error` — 'Invalid printeragent' if name or pairingpin is missing/blank or sort is not an integer. **Example** @@ -75,20 +76,20 @@ Lists the printer agents of the user's selected company. **Returns** -`Promise>` — The printer agents. +`Promise>` — The printer agents. **Example** ```js const agents = await store.printeragent.list(42); -// => [{ id, companyId, name, token, pairingpin, pairedAt, clientserial, clientmodel, clientstate, agentversion, lastseenAt, updatedAt }] +// => [{ id, companyId, name, token, pairingpin, pairedAt, clientserial, clientmodel, clientstate, agentversion, lastseenAt, updatedAt, sort }] ``` ## `printeragent.update` *Source: `src/store/printeragent/update.js`* -Renames a printer agent of the user's selected company. +Updates name and/or sort of a printer agent of the user's selected company. **Parameters** @@ -97,19 +98,20 @@ Renames a printer agent of the user's selected company. | `googleAuthId` | `number` | The id of the Google auth user. | | `input` | `object` | The update input. | | `input.id` | `number` | The printer agent id. | -| `input.name` | `string` | The new display name. | +| `[input.name]` | `string` | The new display name. | +| `[input.sort]` | `number` | The new display order (integer). | | `[tx]` | `object` | Optional transaction to run within. | **Returns** -`Promise<{id: number, companyId: number, name: string, token: string|null, pairingpin: string|null, pairedAt: string|null, clientserial: string|null, clientmodel: string|null, clientstate: string|null, agentversion: string|null, lastseenAt: string|null, updatedAt: string}>` — The updated printer agent. +`Promise<{id: number, companyId: number, name: string, token: string|null, pairingpin: string|null, pairedAt: string|null, clientserial: string|null, clientmodel: string|null, clientstate: string|null, agentversion: string|null, lastseenAt: string|null, updatedAt: string, sort: number}>` — The updated printer agent. **Throws** -`Error` — 'Invalid printeragent' if name is blank; 'Printeragent not found' if id is unknown in the company. +`Error` — 'Invalid printeragent' if name is blank, sort is not an integer, or neither is given; 'Printeragent not found' if id is unknown in the company. **Example** ```js -const agent = await store.printeragent.update(42, { id: 5, name: 'Kitchen printer' }); +const agent = await store.printeragent.update(42, { id: 5, name: 'Kitchen printer', sort: 1 }); ``` diff --git a/scripts/test-printeragent.js b/scripts/test-printeragent.js index b6650d0..a0ec519 100644 --- a/scripts/test-printeragent.js +++ b/scripts/test-printeragent.js @@ -25,6 +25,12 @@ try { assert.equal(a.companyId, company.id); assert.ok(a.updatedAt); + assert.equal(a.sort, 0); + const s1 = await store.printeragent.update(googleAuthId, { id: a.id, sort: 5 }); + assert.equal(s1.sort, 5); + assert.equal(s1.name, 'Bar'); + await assert.rejects(store.printeragent.update(googleAuthId, { id: a.id }), /Invalid printeragent/); + await assert.rejects(store.printeragent.update(googleAuthId, { id: a.id, sort: 1.5 }), /Invalid printeragent/); const b = await store.printeragent.update(googleAuthId, { id: a.id, name: 'Kitchen' }); assert.equal(b.name, 'Kitchen'); await assert.rejects(store.printeragent.update(googleAuthId, { id: 9999, name: 'x' }), /Printeragent not found/); diff --git a/src/providers/mssql/queries/printeragent.js b/src/providers/mssql/queries/printeragent.js index 7f54b94..76c672e 100644 --- a/src/providers/mssql/queries/printeragent.js +++ b/src/providers/mssql/queries/printeragent.js @@ -10,7 +10,8 @@ export const PRINTERAGENT_COLUMNS = ` clientstate, agentversion, lastseen_at AS lastseenAt, - updated_at AS updatedAt + updated_at AS updatedAt, + sort `; export const LIST_BY_COMPANY = ` @@ -18,7 +19,7 @@ SELECT ${PRINTERAGENT_COLUMNS} FROM printeragent WHERE company_id = @companyId -ORDER BY name, id +ORDER BY sort, id `; export const GET_BY_COMPANY_AND_ID = ` @@ -29,7 +30,7 @@ WHERE id = @id AND company_id = @companyId `; export const CREATE = ` -INSERT INTO printeragent (company_id, name, pairingpin) +INSERT INTO printeragent (company_id, name, pairingpin, sort) OUTPUT INSERTED.id, INSERTED.company_id AS companyId, @@ -42,13 +43,14 @@ OUTPUT INSERTED.clientstate, INSERTED.agentversion, INSERTED.lastseen_at AS lastseenAt, - INSERTED.updated_at AS updatedAt -VALUES (@companyId, @name, @pairingpin) + INSERTED.updated_at AS updatedAt, + INSERTED.sort +VALUES (@companyId, @name, @pairingpin, @sort) `; -export const UPDATE_NAME = ` +export const UPDATE = ` UPDATE printeragent -SET name = @name, updated_at = SYSUTCDATETIME() +SET name = @name, sort = @sort, updated_at = SYSUTCDATETIME() WHERE id = @id AND company_id = @companyId `; diff --git a/src/providers/mssql/schema.js b/src/providers/mssql/schema.js index 0aad2c2..c21bff2 100644 --- a/src/providers/mssql/schema.js +++ b/src/providers/mssql/schema.js @@ -1027,6 +1027,7 @@ BEGIN agentversion NVARCHAR(255) NULL, lastseen_at DATETIME2 NULL, updated_at DATETIME2 NOT NULL CONSTRAINT DF_printeragent_updated_at DEFAULT SYSUTCDATETIME(), + sort INT NOT NULL CONSTRAINT DF_printeragent_sort DEFAULT 0, CONSTRAINT FK_printeragent_company FOREIGN KEY (company_id) REFERENCES company(id) ON DELETE CASCADE ); CREATE UNIQUE INDEX UQ_printeragent_token ON printeragent(token) WHERE token IS NOT NULL; diff --git a/src/providers/sqlite3/queries/printeragent.js b/src/providers/sqlite3/queries/printeragent.js index 71dee6f..0b2b8f8 100644 --- a/src/providers/sqlite3/queries/printeragent.js +++ b/src/providers/sqlite3/queries/printeragent.js @@ -8,5 +8,5 @@ export const { GET_BY_COMPANY_AND_ID, LIST_BY_COMPANY, PRINTERAGENT_COLUMNS, - UPDATE_NAME, + UPDATE, } = queries; diff --git a/src/providers/sqlite3/schema.js b/src/providers/sqlite3/schema.js index 009258b..6090557 100644 --- a/src/providers/sqlite3/schema.js +++ b/src/providers/sqlite3/schema.js @@ -706,6 +706,7 @@ CREATE TABLE IF NOT EXISTS printeragent ( agentversion TEXT, lastseen_at TEXT, updated_at TEXT NOT NULL DEFAULT (${UTC_NOW}), + sort INTEGER NOT NULL DEFAULT 0, FOREIGN KEY (company_id) REFERENCES company(id) ON DELETE CASCADE ); CREATE UNIQUE INDEX IF NOT EXISTS UQ_printeragent_token diff --git a/src/store/printeragent/_shared.js b/src/store/printeragent/_shared.js index fd2efa8..b0f2443 100644 --- a/src/store/printeragent/_shared.js +++ b/src/store/printeragent/_shared.js @@ -12,8 +12,9 @@ export function shape(row) { agentversion: row.agentversion ?? null, lastseenAt: row.lastseenAt ?? null, updatedAt: row.updatedAt, + sort: row.sort, }; } export const PRINTERAGENT_TYPEDEF = - '{id: number, companyId: number, name: string, token: string|null, pairingpin: string|null, pairedAt: string|null, clientserial: string|null, clientmodel: string|null, clientstate: string|null, agentversion: string|null, lastseenAt: string|null, updatedAt: string}'; + '{id: number, companyId: number, name: string, token: string|null, pairingpin: string|null, pairedAt: string|null, clientserial: string|null, clientmodel: string|null, clientstate: string|null, agentversion: string|null, lastseenAt: string|null, updatedAt: string, sort: number}'; diff --git a/src/store/printeragent/create.js b/src/store/printeragent/create.js index 533b0b3..6194124 100644 --- a/src/store/printeragent/create.js +++ b/src/store/printeragent/create.js @@ -20,9 +20,10 @@ export default function create(provider) { * @param {object} input - The printer agent input. * @param {string} input.name - The display name. * @param {string} input.pairingpin - The pairing pin the agent uses to register. + * @param {number} [input.sort] - Display order (integer, ascending). Default 0. * @param {object} [tx] - Optional transaction to run within. - * @returns {Promise<{id: number, companyId: number, name: string, token: string|null, pairingpin: string|null, pairedAt: string|null, clientserial: string|null, clientmodel: string|null, clientstate: string|null, agentversion: string|null, lastseenAt: string|null, updatedAt: string}>} The created printer agent. - * @throws {Error} 'Invalid printeragent' if name or pairingpin is missing/blank. + * @returns {Promise<{id: number, companyId: number, name: string, token: string|null, pairingpin: string|null, pairedAt: string|null, clientserial: string|null, clientmodel: string|null, clientstate: string|null, agentversion: string|null, lastseenAt: string|null, updatedAt: string, sort: number}>} The created printer agent. + * @throws {Error} 'Invalid printeragent' if name or pairingpin is missing/blank or sort is not an integer. * * @example * const agent = await store.printeragent.create(42, { name: 'Bar printer', pairingpin: '123456' }); @@ -34,10 +35,15 @@ export default function create(provider) { throw new Error('Invalid printeragent'); } + if (input.sort !== undefined && !Number.isInteger(input.sort)) { + throw new Error('Invalid printeragent'); + } + const sort = input.sort ?? 0; + const runner = makeRunner(provider, tx); const { companyId } = await access(googleAuthId, runner); - const result = await runner.query(sql, { companyId, name, pairingpin }); + const result = await runner.query(sql, { companyId, name, pairingpin, sort }); return shape(result.rows[0]); }; } diff --git a/src/store/printeragent/list.js b/src/store/printeragent/list.js index db02e44..5bbe5fc 100644 --- a/src/store/printeragent/list.js +++ b/src/store/printeragent/list.js @@ -13,11 +13,11 @@ export default function list(provider) { * * @param {number} googleAuthId - The id of the Google auth user. * @param {object} [tx] - Optional transaction to run within. - * @returns {Promise>} The printer agents. + * @returns {Promise>} The printer agents. * * @example * const agents = await store.printeragent.list(42); - * // => [{ id, companyId, name, token, pairingpin, pairedAt, clientserial, clientmodel, clientstate, agentversion, lastseenAt, updatedAt }] + * // => [{ id, companyId, name, token, pairingpin, pairedAt, clientserial, clientmodel, clientstate, agentversion, lastseenAt, updatedAt, sort }] */ return async (googleAuthId, tx) => { const runner = makeRunner(provider, tx); diff --git a/src/store/printeragent/update.js b/src/store/printeragent/update.js index a2584a6..344f79e 100644 --- a/src/store/printeragent/update.js +++ b/src/store/printeragent/update.js @@ -1,10 +1,10 @@ import { GET_BY_COMPANY_AND_ID as MSSQL_GET, - UPDATE_NAME as MSSQL_UPDATE_NAME, + UPDATE as MSSQL_UPDATE, } from '../../providers/mssql/queries/printeragent.js'; import { GET_BY_COMPANY_AND_ID as SQLITE_GET, - UPDATE_NAME as SQLITE_UPDATE_NAME, + UPDATE as SQLITE_UPDATE, } from '../../providers/sqlite3/queries/printeragent.js'; import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyAccess.js'; import makeRunner from '../runner.js'; @@ -14,40 +14,53 @@ export default function update(provider) { const access = resolveSelectedCompanyAccess(provider); const isSqlite = provider?.dialect === 'sqlite3'; const getSql = isSqlite ? SQLITE_GET : MSSQL_GET; - const updateNameSql = isSqlite ? SQLITE_UPDATE_NAME : MSSQL_UPDATE_NAME; + const updateSql = isSqlite ? SQLITE_UPDATE : MSSQL_UPDATE; /** - * Renames a printer agent of the user's selected company. + * Updates name and/or sort of a printer agent of the user's selected company. * * @param {number} googleAuthId - The id of the Google auth user. * @param {object} input - The update input. * @param {number} input.id - The printer agent id. - * @param {string} input.name - The new display name. + * @param {string} [input.name] - The new display name. + * @param {number} [input.sort] - The new display order (integer). * @param {object} [tx] - Optional transaction to run within. - * @returns {Promise<{id: number, companyId: number, name: string, token: string|null, pairingpin: string|null, pairedAt: string|null, clientserial: string|null, clientmodel: string|null, clientstate: string|null, agentversion: string|null, lastseenAt: string|null, updatedAt: string}>} The updated printer agent. - * @throws {Error} 'Invalid printeragent' if name is blank; 'Printeragent not found' if id is unknown in the company. + * @returns {Promise<{id: number, companyId: number, name: string, token: string|null, pairingpin: string|null, pairedAt: string|null, clientserial: string|null, clientmodel: string|null, clientstate: string|null, agentversion: string|null, lastseenAt: string|null, updatedAt: string, sort: number}>} The updated printer agent. + * @throws {Error} 'Invalid printeragent' if name is blank, sort is not an integer, or neither is given; 'Printeragent not found' if id is unknown in the company. * * @example - * const agent = await store.printeragent.update(42, { id: 5, name: 'Kitchen printer' }); + * const agent = await store.printeragent.update(42, { id: 5, name: 'Kitchen printer', sort: 1 }); */ return async (googleAuthId, input = {}, tx) => { const id = Number(input.id); if (!Number.isInteger(id) || id <= 0) { throw new Error('Printeragent not found'); } - const name = typeof input.name === 'string' ? input.name.trim() : ''; - if (!name) { + if (input.name === undefined && input.sort === undefined) { + throw new Error('Invalid printeragent'); + } + if (input.name !== undefined && (typeof input.name !== 'string' || !input.name.trim())) { + throw new Error('Invalid printeragent'); + } + if (input.sort !== undefined && !Number.isInteger(input.sort)) { throw new Error('Invalid printeragent'); } const runner = makeRunner(provider, tx); const { companyId } = await access(googleAuthId, runner); - const result = await runner.query(updateNameSql, { id, companyId, name }); - if (!result.rowsAffected) { + const existing = await runner.query(getSql, { id, companyId }); + if (!existing.rows[0]) { throw new Error('Printeragent not found'); } + await runner.query(updateSql, { + id, + companyId, + name: input.name !== undefined ? input.name.trim() : existing.rows[0].name, + sort: input.sort !== undefined ? input.sort : existing.rows[0].sort, + }); + const row = await runner.query(getSql, { id, companyId }); return shape(row.rows[0]); };