This commit is contained in:
seb
2026-10-09 18:10:51 +02:00
parent 4ae3439289
commit 07c4f0bff9
8 changed files with 130 additions and 43 deletions

View File

@@ -39,7 +39,7 @@ Creates a brand for the user's selected company.
**Returns**
`Promise<{id: number, name: string, gsprpinfo: string|null, pics: Array<{id: number, sort: number, fullHash: string, thumbW: number, thumbH: number, thumbHash: string}>}>` — The created brand.
`Promise<{id: number, name: string, gsprpinfo: string|null, pics: Array<{fullHash: string, thumbW: number, thumbH: number, thumbHash: string}>}>` — The created brand.
**Throws**
@@ -84,24 +84,27 @@ await store.brand.delete(42, 3);
*Source: `src/store/brand/list.js`*
Lists the brands of the user's selected company.
Lists the brands of the user's selected company, sorted by name.
**Parameters**
| Name | Type | Description |
| --- | --- | --- |
| `googleAuthId` | `number` | The id of the Google auth user. |
| `[opts]` | `object` | Optional pagination options. |
| `[opts.offset=0]` | `number` | Number of brands to skip. |
| `[opts.limit=10]` | `number` | Number of brands to return (max 100). |
| `[tx]` | `object` | Optional transaction to run within. |
**Returns**
`Promise<Array<{id: number, name: string, gsprpinfo: string|null, pics: Array<{id: number, sort: number, fullHash: string, thumbW: number, thumbH: number, thumbHash: string}>}>>` — The brands.
`Promise<{brands: Array<{id: number, name: string, gsprpinfo: string|null, pics: Array<{fullHash: string, thumbW: number, thumbH: number, thumbHash: string}>}>, total: number, offset: number, limit: number}>` — The brands plus the total unpaged count.
**Example**
```js
const brands = await store.brand.list(42);
// => [{ id, name, gsprpinfo, pics: [{ sort, fullHash, thumbW, thumbH, thumbHash }] }]
const result = await store.brand.list(42, { offset: 0, limit: 10 });
// => { brands: [{ id, name, gsprpinfo, pics: [...] }], total: 25, offset: 0, limit: 10 }
```
## `brand.update`
@@ -154,7 +157,7 @@ Creates or updates a brand for the user's selected company, keyed by name.
**Returns**
`Promise<{id: number, name: string, gsprpinfo: string|null, pics: Array<{id: number, sort: number, fullHash: string, thumbW: number, thumbH: number, thumbHash: string}>}>` — The upserted brand.
`Promise<{id: number, name: string, gsprpinfo: string|null, pics: Array<{fullHash: string, thumbW: number, thumbH: number, thumbHash: string}>}>` — The upserted brand.
**Throws**

6
API.md
View File

@@ -130,11 +130,11 @@ The store is exposed as a `LibDashStore` instance (see `src/index.js`). Each met
- `set(googleAuthId: number, input: {key: string, value: string, frontend?: boolean, writeonly?: boolean}, tx?: object): Promise<{key: string, value: string, frontend: boolean, writeonly: boolean}>`
- [brand](API.brand.md)
- `addImage(googleAuthId: number, brandId: number, image: Buffer, tx?: object): Promise<{id: number, fullHash: string, thumbW: number, thumbH: number, thumbHash: string, pics: Array<object>}>`
- `create(googleAuthId: number, input: {name: string, gsprpinfo?: string}, tx?: object): Promise<{id: number, name: string, gsprpinfo: string|null, pics: Array<{id: number, sort: number, fullHash: string, thumbW: number, thumbH: number, thumbHash: string}>}>`
- `create(googleAuthId: number, input: {name: string, gsprpinfo?: string}, tx?: object): Promise<{id: number, name: string, gsprpinfo: string|null, pics: Array<{fullHash: string, thumbW: number, thumbH: number, thumbHash: string}>}>`
- `delete(googleAuthId: number, id: number, tx?: object): Promise<void>`
- `list(googleAuthId: number, tx?: object): Promise<Array<{id: number, name: string, gsprpinfo: string|null, pics: Array<{id: number, sort: number, fullHash: string, thumbW: number, thumbH: number, thumbHash: string}>}>>`
- `list(googleAuthId: number, opts?: {offset?: number, limit?: number}, tx?: object): Promise<{brands: Array<{id: number, name: string, gsprpinfo: string|null, pics: Array<{fullHash: string, thumbW: number, thumbH: number, thumbHash: string}>}>, total: number, offset: number, limit: number}>`
- `update(googleAuthId: number, id: number, input: {name?: string, gsprpinfo?: string}, tx?: object): Promise<{id: number, name: string, gsprpinfo: string|null, touchedArticleCount: number}>`
- `upsert(googleAuthId: number, input: {name: string, gsprpinfo?: string}, tx?: object): Promise<{id: number, name: string, gsprpinfo: string|null, pics: Array<{id: number, sort: number, fullHash: string, thumbW: number, thumbH: number, thumbHash: string}>}>`
- `upsert(googleAuthId: number, input: {name: string, gsprpinfo?: string}, tx?: object): Promise<{id: number, name: string, gsprpinfo: string|null, pics: Array<{fullHash: string, thumbW: number, thumbH: number, thumbHash: string}>}>`
- [customergroup](API.customergroup.md)
- `create(googleAuthId: number, input: {name: string}, tx?: object): Promise<{id: number, name: string, sort: number}>`
- `delete(googleAuthId: number, id: number, tx?: object): Promise<void>`

View File

@@ -5,8 +5,6 @@ SELECT
b.gsprpinfo,
(
SELECT
bp.id,
bp.sort,
p.hash AS fullHash,
p.thumbw AS thumbW,
p.thumbh AS thumbH,
@@ -22,6 +20,35 @@ WHERE b.company_id = @companyId
ORDER BY b.name
`;
export const COUNT_BY_COMPANY = `
SELECT COUNT(*) AS total
FROM brand b
WHERE b.company_id = @companyId
`;
export const LIST_BY_COMPANY_PAGED = `
SELECT
b.id,
b.name,
b.gsprpinfo,
(
SELECT
p.hash AS fullHash,
p.thumbw AS thumbW,
p.thumbh AS thumbH,
p.thumbhash AS thumbHash
FROM brandpic bp
INNER JOIN pic p ON p.id = bp.pic_id
WHERE bp.brand_id = b.id
ORDER BY bp.sort ASC, bp.id ASC
FOR JSON PATH
) AS picsJson
FROM brand b
WHERE b.company_id = @companyId
ORDER BY b.name
OFFSET @offset ROWS FETCH NEXT @limit ROWS ONLY
`;
export const GET_BY_COMPANY_AND_NAME = `
SELECT
b.id,
@@ -29,8 +56,6 @@ SELECT
b.gsprpinfo,
(
SELECT
bp.id,
bp.sort,
p.hash AS fullHash,
p.thumbw AS thumbW,
p.thumbh AS thumbH,

View File

@@ -4,6 +4,7 @@ import { translateQueries } from './translate.js';
const queries = translateQueries(source, 'brand');
export const {
CREATE, DELETE_BY_COMPANY_AND_ID, UPDATE_BY_ID, GET_BY_ID_AND_COMPANY,
COUNT_BY_COMPANY,
} = queries;
export const LIST_BY_COMPANY = `
@@ -13,15 +14,13 @@ SELECT
b.gsprpinfo,
(
SELECT COALESCE(json_group_array(json_object(
'id', id,
'sort', sort,
'fullHash', hash,
'thumbW', thumbw,
'thumbH', thumbh,
'thumbHash', thumbhash
)), '[]')
FROM (
SELECT bp.id, bp.sort, p.hash, p.thumbw, p.thumbh, p.thumbhash
SELECT p.hash, p.thumbw, p.thumbh, p.thumbhash
FROM brandpic bp
INNER JOIN pic p ON p.id = bp.pic_id
WHERE bp.brand_id = b.id
@@ -33,6 +32,32 @@ WHERE b.company_id = @companyId
ORDER BY b.name
`;
export const LIST_BY_COMPANY_PAGED = `
SELECT
b.id,
b.name,
b.gsprpinfo,
(
SELECT COALESCE(json_group_array(json_object(
'fullHash', hash,
'thumbW', thumbw,
'thumbH', thumbh,
'thumbHash', thumbhash
)), '[]')
FROM (
SELECT p.hash, p.thumbw, p.thumbh, p.thumbhash
FROM brandpic bp
INNER JOIN pic p ON p.id = bp.pic_id
WHERE bp.brand_id = b.id
ORDER BY bp.sort, bp.id
)
) AS picsJson
FROM brand b
WHERE b.company_id = @companyId
ORDER BY b.name
LIMIT @limit OFFSET @offset
`;
export const GET_BY_COMPANY_AND_NAME = `
SELECT
b.id,
@@ -40,15 +65,13 @@ SELECT
b.gsprpinfo,
(
SELECT COALESCE(json_group_array(json_object(
'id', id,
'sort', sort,
'fullHash', hash,
'thumbW', thumbw,
'thumbH', thumbh,
'thumbHash', thumbhash
)), '[]')
FROM (
SELECT bp.id, bp.sort, p.hash, p.thumbw, p.thumbh, p.thumbhash
SELECT p.hash, p.thumbw, p.thumbh, p.thumbhash
FROM brandpic bp
INNER JOIN pic p ON p.id = bp.pic_id
WHERE bp.brand_id = b.id

View File

@@ -82,11 +82,8 @@ export default function addImage(provider) {
picId,
sort,
});
const brandPicId = brandPicResult.rows[0]?.id ?? brandPicResult.insertId;
const picObj = {
id: brandPicId,
sort,
fullHash,
thumbW: processed.thumb.w,
thumbH: processed.thumb.h,

View File

@@ -26,7 +26,7 @@ export default function create(provider) {
* @param {string} input.name - The brand name.
* @param {string} [input.gsprpinfo] - Optional GSPRP info.
* @param {object} [tx] - Optional transaction to run within.
* @returns {Promise<{id: number, name: string, gsprpinfo: string|null, pics: Array<{id: number, sort: number, fullHash: string, thumbW: number, thumbH: number, thumbHash: string}>}>} The created brand.
* @returns {Promise<{id: number, name: string, gsprpinfo: string|null, pics: Array<{fullHash: string, thumbW: number, thumbH: number, thumbHash: string}>}>} The created brand.
* @throws {Error} If the brand is invalid or already exists.
*
* @example

View File

@@ -1,5 +1,13 @@
import { LIST_BY_COMPANY as MSSQL_LIST_BY_COMPANY } from '../../providers/mssql/queries/brand.js';
import { LIST_BY_COMPANY as SQLITE_LIST_BY_COMPANY } from '../../providers/sqlite3/queries/brand.js';
import {
COUNT_BY_COMPANY as MSSQL_COUNT,
LIST_BY_COMPANY as MSSQL_LIST_BY_COMPANY,
LIST_BY_COMPANY_PAGED as MSSQL_LIST_PAGED,
} from '../../providers/mssql/queries/brand.js';
import {
COUNT_BY_COMPANY as SQLITE_COUNT,
LIST_BY_COMPANY as SQLITE_LIST_BY_COMPANY,
LIST_BY_COMPANY_PAGED as SQLITE_LIST_PAGED,
} from '../../providers/sqlite3/queries/brand.js';
import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyAccess.js';
import makeRunner from '../runner.js';
@@ -11,32 +19,63 @@ function parsePics(raw) {
export default function list(provider) {
const access = resolveSelectedCompanyAccess(provider);
const sql =
provider?.dialect === 'sqlite3'
? SQLITE_LIST_BY_COMPANY
: MSSQL_LIST_BY_COMPANY;
const isSqlite = provider?.dialect === 'sqlite3';
const sql = isSqlite ? SQLITE_LIST_BY_COMPANY : MSSQL_LIST_BY_COMPANY;
const sqlCount = isSqlite ? SQLITE_COUNT : MSSQL_COUNT;
const sqlPaged = isSqlite ? SQLITE_LIST_PAGED : MSSQL_LIST_PAGED;
/**
* Lists the brands of the user's selected company.
* Lists the brands of the user's selected company, sorted by name.
*
* @param {number} googleAuthId - The id of the Google auth user.
* @param {object} [opts] - Optional pagination options.
* @param {number} [opts.offset=0] - Number of brands to skip.
* @param {number} [opts.limit=10] - Number of brands to return (max 100).
* @param {object} [tx] - Optional transaction to run within.
* @returns {Promise<Array<{id: number, name: string, gsprpinfo: string|null, pics: Array<{id: number, sort: number, fullHash: string, thumbW: number, thumbH: number, thumbHash: string}>}>>} The brands.
* @returns {Promise<{brands: Array<{id: number, name: string, gsprpinfo: string|null, pics: Array<{fullHash: string, thumbW: number, thumbH: number, thumbHash: string}>}>, total: number, offset: number, limit: number}>} The brands plus the total unpaged count.
*
* @example
* const brands = await store.brand.list(42);
* // => [{ id, name, gsprpinfo, pics: [{ sort, fullHash, thumbW, thumbH, thumbHash }] }]
* const result = await store.brand.list(42, { offset: 0, limit: 10 });
* // => { brands: [{ id, name, gsprpinfo, pics: [...] }], total: 25, offset: 0, limit: 10 }
*/
return async (googleAuthId, tx) => {
const { companyId } = await access(googleAuthId, tx);
return async (googleAuthId, opts, tx) => {
let offset = 0;
let limit = 10;
let resolvedTx = tx;
const result = await makeRunner(provider, tx).query(sql, { companyId });
if (opts != null && typeof opts === 'object' && !opts._isTx) {
const parsedOffset = Number(opts.offset ?? 0);
const parsedLimit = Number(opts.limit ?? 10);
if (!Number.isInteger(parsedOffset) || parsedOffset < 0) {
throw new Error('Invalid offset');
}
if (!Number.isInteger(parsedLimit) || parsedLimit < 1 || parsedLimit > 100) {
throw new Error('Invalid limit');
}
offset = parsedOffset;
limit = parsedLimit;
} else if (opts != null) {
resolvedTx = opts;
}
return result.rows.map((row) => ({
id: row.id,
name: row.name,
gsprpinfo: row.gsprpinfo,
pics: parsePics(row.picsJson),
}));
const runner = makeRunner(provider, resolvedTx);
const { companyId } = await access(googleAuthId, runner);
const [countResult, listResult] = await Promise.all([
runner.query(sqlCount, { companyId }),
runner.query(sqlPaged, { companyId, offset, limit }),
]);
return {
brands: listResult.rows.map((row) => ({
id: row.id,
name: row.name,
gsprpinfo: row.gsprpinfo,
pics: parsePics(row.picsJson),
})),
total: Number(countResult.rows[0]?.total ?? 0),
offset,
limit,
};
};
}

View File

@@ -34,7 +34,7 @@ export default function upsert(provider) {
* @param {string} input.name - The brand name.
* @param {string} [input.gsprpinfo] - Optional GSPRP info.
* @param {object} [tx] - Optional transaction to run within.
* @returns {Promise<{id: number, name: string, gsprpinfo: string|null, pics: Array<{id: number, sort: number, fullHash: string, thumbW: number, thumbH: number, thumbHash: string}>}>} The upserted brand.
* @returns {Promise<{id: number, name: string, gsprpinfo: string|null, pics: Array<{fullHash: string, thumbW: number, thumbH: number, thumbHash: string}>}>} The upserted brand.
* @throws {Error} If the brand is invalid.
*
* @example