diff --git a/README.md b/README.md index 4b684e8..ba1baa0 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,8 @@ const client = await new LibDashStore(pool); // runs initialize() (ensure schema SQL lives in `src/sql/.js`. Schema DDL: `src/schema.js` / `import 'libdashstore/schema'`. +ID fields in the JS API use camelCase `…Id` (DB columns stay snake_case). + --- ## Schema @@ -27,11 +29,10 @@ SQL lives in `src/sql/
.js`. Schema DDL: `src/schema.js` / `import 'libdas | Method | Signature | Returns | | --- | --- | --- | -| `create` | `({ iso639, name, taxrate, reducedtaxrate })` | `{ id, root_category_id, language_id, taxrate_id, reducedtaxrate_id, taxfreerate_id }` | -| `update` | `(id, { iso639, name, taxrate, reducedtaxrate })` | `{ id, root_category_id, language_id, taxrate_id, reducedtaxrate_id, taxfreerate_id }` | +| `create` | `({ iso639, name, taxrate, reducedtaxrate })` | `{ id, rootCategoryId, languageId, taxrateId, reducedtaxrateId, taxfreerateId }` | +| `update` | `(id, { iso639, name, taxrate, reducedtaxrate })` | `{ id, rootCategoryId, languageId, taxrateId, reducedtaxrateId, taxfreerateId }` | | `delete` | `(companyId)` | `void` | -| `list` | `()` | `{ id, root_category_id, language_id, taxrate_id, reducedtaxrate_id, taxfreerate_id }[]` | - +| `list` | `()` | `{ id, rootCategoryId, languageId, taxrateId, reducedtaxrateId, taxfreerateId }[]` | ### `create` (transaction) @@ -39,8 +40,8 @@ For `POST /api/company`: 1. Fails if `name` is taken (`Company name not available`) 2. Ensures language by `iso639` (get or create) -3. Ensures tax rows for `taxrate`, `reducedtaxrate`, and `0` → `taxfreerate_id` -4. Creates root category (`parent_id = null`) +3. Ensures tax rows for `taxrate`, `reducedtaxrate`, and `0` → `taxfreerateId` +4. Creates root category (`parentId = null`) 5. Inserts company with all FKs set ### `update` (transaction) @@ -50,7 +51,7 @@ For `PATCH /api/company/:id`: 1. Fails if company missing / name taken by another company 2. Ensures lang + tax rates 3. Updates `name`, `default_language_id`, `taxrate`, `reducedtaxrate` - (`taxfreerate_id` / root category unchanged) + (`taxfreerateId` / root category unchanged) --- @@ -58,28 +59,16 @@ For `PATCH /api/company/:id`: | Method | Signature | Returns | | --- | --- | --- | -| `add` | `(email)` | `number` (auth id) | -| `getById` | `(id)` | `row \| undefined` | -| `getByEmail` | `(email)` | `row \| undefined` | +| `add` | `(email)` | `id` | +| `getByEmail` | `(email)` | `{ id, email, createdAt, selectedCompanyId } \| undefined` | +| `listAllowedEmails` | `()` | `{ id, email, createdAt, selectedCompanyId }[]` | +| `removeAllowedEmail` | `(email)` | `void` | | `setSelectedCompany` | `(authId, companyId)` | `void` | -| `list` | `()` | `row[]` | -| `del` | `(id)` | `void` | - -### Row shape - -```ts -{ - id: number - email: string - created_at: Date - selected_company: number | null -} -``` Backend mapping: -- Login: `getByEmail(email)` ← `getAllowedEmail` -- Auth middleware: `getById(id)` ← `getAllowedEmailById` +- Login: `getByEmail(email)` +- CLI user manager: `listAllowedEmails()` / `removeAllowedEmail(email)` / `add(email)` - `PUT /api/auth/selected-company`: `setSelectedCompany(authId, companyId)` --- @@ -88,8 +77,8 @@ Backend mapping: | Method | Signature | Returns | | --- | --- | --- | -| `add` | `(companyId, authId)` | `number` (companyuser id) | -| `getByEmail` | `(email)` | `number[]` (companyuser ids) | +| `add` | `(companyId, authId)` | `id` | +| `getByEmail` | `(email)` | `id[]` | | `delete` | `(id)` | `void` | --- @@ -98,9 +87,9 @@ Backend mapping: | Method | Signature | Returns | | --- | --- | --- | -| `create` | `(iso639)` | `number` | +| `create` | `(iso639)` | `id` | | `getByIso639` | `(iso639)` | `{ id, iso639 } \| undefined` | -| `ensure` | `(iso639, tx?)` | `number` (existing or new id) | +| `ensure` | `(iso639, tx?)` | `id` | | `list` | `()` | `{ id, iso639 }[]` | | `delete` | `(id)` | `void` | @@ -110,9 +99,9 @@ Backend mapping: | Method | Signature | Returns | | --- | --- | --- | -| `create` | `(rate)` | `number` | +| `create` | `(rate)` | `id` | | `getByRate` | `(rate)` | `{ id, rate } \| undefined` | -| `ensure` | `(rate, tx?)` | `number` (existing or new id) | +| `ensure` | `(rate, tx?)` | `id` | | `list` | `()` | `{ id, rate }[]` | --- @@ -121,10 +110,10 @@ Backend mapping: | Method | Signature | Returns | | --- | --- | --- | -| `create` | `({ parent_id, sort })` | `number` | +| `create` | `({ parentId, sort })` | `id` | | `updateParentId` | `(id, parentId)` | `void` | | `delete` | `(id)` | `void` | -| `getTree` | `(rootId)` | `{ id, parent_id, sort }[]` (flat CTE result) | +| `getTree` | `(rootId)` | `{ id, parentId, sort }[]` | --- diff --git a/scripts/test-api.js b/scripts/test-api.js index 074d407..9ed89d9 100644 --- a/scripts/test-api.js +++ b/scripts/test-api.js @@ -95,7 +95,7 @@ const MENU = { category: { label: 'category', items: [ - { value: 'category.create', label: 'create', hint: '{parent_id, sort} → id' }, + { value: 'category.create', label: 'create', hint: '{parentId, sort} → id' }, { value: 'category.updateParentId', label: 'updateParentId', hint: 'id, parentId' }, { value: 'category.delete', label: 'delete', hint: 'id' }, { value: 'category.getTree', label: 'getTree', hint: 'rootId → rows' }, @@ -112,12 +112,11 @@ const MENU = { google_auth: { label: 'google_auth', items: [ - { value: 'google_auth.add', label: 'add', hint: 'email → authId' }, - { value: 'google_auth.getById', label: 'getById', hint: 'id → row' }, + { value: 'google_auth.add', label: 'add', hint: 'email → id' }, { value: 'google_auth.getByEmail', label: 'getByEmail', hint: 'email → row' }, + { value: 'google_auth.listAllowedEmails', label: 'listAllowedEmails', hint: '→ rows' }, + { value: 'google_auth.removeAllowedEmail', label: 'removeAllowedEmail', hint: 'email' }, { value: 'google_auth.setSelectedCompany', label: 'setSelectedCompany', hint: 'authId, companyId' }, - { value: 'google_auth.list', label: 'list', hint: '→ rows' }, - { value: 'google_auth.del', label: 'del', hint: 'id' }, ], }, schema: { @@ -186,13 +185,10 @@ async function runMethod(client, action) { reducedtaxrate, }); const companyId = created.id; - - const auths = await client.google_auth.list(); - const existingAuth = auths.find((row) => row.email === email); - const authId = existingAuth - ? existingAuth.id + const existing = await client.google_auth.getByEmail(email); + const authId = existing + ? existing.id : await client.google_auth.add(email); - const companyUserId = await client.companyuser.add(companyId, authId); await client.google_auth.setSelectedCompany(authId, companyId); @@ -207,7 +203,6 @@ async function runMethod(client, action) { iso639, taxrate, reducedtaxrate, - authCreated: !existingAuth, }, ]; } @@ -298,7 +293,7 @@ async function runMethod(client, action) { return ['ok', true]; } case 'category.create': { - const parentRaw = await askText('parent_id (null as empty)', { + const parentRaw = await askText('parentId (null as empty)', { placeholder: '', validate: (v) => v?.trim() && !/^\d+$/.test(v.trim()) ? 'Integer or empty' : undefined, @@ -306,13 +301,13 @@ async function runMethod(client, action) { if (parentRaw == null) return; const sortRaw = await askInt('sort', '0'); if (sortRaw == null) return; - const parent_id = parentRaw === '' ? null : Number(parentRaw); - return ['id', await client.category.create({ parent_id, sort: Number(sortRaw) })]; + const parentId = parentRaw === '' ? null : Number(parentRaw); + return ['id', await client.category.create({ parentId, sort: Number(sortRaw) })]; } case 'category.updateParentId': { const id = await askInt('Category id'); if (id == null) return; - const parentRaw = await askText('parent_id (null as empty)', { + const parentRaw = await askText('parentId (null as empty)', { placeholder: '', validate: (v) => v?.trim() && !/^\d+$/.test(v.trim()) ? 'Integer or empty' : undefined, @@ -360,12 +355,7 @@ async function runMethod(client, action) { validate: (v) => (!v?.trim() ? 'Required' : undefined), }); if (email == null) return; - return ['authId', await client.google_auth.add(email)]; - } - case 'google_auth.getById': { - const id = await askInt('Auth id'); - if (id == null) return; - return ['row', await client.google_auth.getById(Number(id))]; + return ['id', await client.google_auth.add(email)]; } case 'google_auth.getByEmail': { const email = await askText('Email', { @@ -375,6 +365,18 @@ async function runMethod(client, action) { if (email == null) return; return ['row', await client.google_auth.getByEmail(email)]; } + case 'google_auth.listAllowedEmails': { + return ['rows', await client.google_auth.listAllowedEmails()]; + } + case 'google_auth.removeAllowedEmail': { + const email = await askText('Email', { + placeholder: 'user@example.com', + validate: (v) => (!v?.trim() ? 'Required' : undefined), + }); + if (email == null) return; + await client.google_auth.removeAllowedEmail(email); + return ['ok', true]; + } case 'google_auth.setSelectedCompany': { const id = await askInt('Auth id'); if (id == null) return; @@ -383,15 +385,6 @@ async function runMethod(client, action) { await client.google_auth.setSelectedCompany(Number(id), Number(companyId)); return ['ok', true]; } - case 'google_auth.list': { - return ['rows', await client.google_auth.list()]; - } - case 'google_auth.del': { - const id = await askInt('Auth id'); - if (id == null) return; - await client.google_auth.del(Number(id)); - return ['ok', true]; - } case 'initialize': { await client.initialize(); return ['ok', true]; diff --git a/src/index.js b/src/index.js index 7b9a10b..5cc423a 100644 --- a/src/index.js +++ b/src/index.js @@ -19,11 +19,10 @@ import taxGetByRate from './store/tax/getByRate.js'; import taxEnsure from './store/tax/ensure.js'; import taxList from './store/tax/list.js'; import googleAuthAdd from './store/google_auth/add.js'; -import googleAuthGetById from './store/google_auth/getById.js'; import googleAuthGetByEmail from './store/google_auth/getByEmail.js'; +import googleAuthListAllowedEmails from './store/google_auth/listAllowedEmails.js'; +import googleAuthRemoveAllowedEmail from './store/google_auth/removeAllowedEmail.js'; import googleAuthSetSelectedCompany from './store/google_auth/setSelectedCompany.js'; -import googleAuthList from './store/google_auth/list.js'; -import googleAuthDel from './store/google_auth/del.js'; import storePurge from './store/purge.js'; import storeInitialize from './store/initialize.js'; @@ -68,11 +67,10 @@ export default class LibDashStore { this.google_auth = { add: googleAuthAdd(pool), - getById: googleAuthGetById(pool), getByEmail: googleAuthGetByEmail(pool), + listAllowedEmails: googleAuthListAllowedEmails(pool), + removeAllowedEmail: googleAuthRemoveAllowedEmail(pool), setSelectedCompany: googleAuthSetSelectedCompany(pool), - list: googleAuthList(pool), - del: googleAuthDel(pool), }; this.purge = storePurge(pool); diff --git a/src/sql/category.js b/src/sql/category.js index 57f6b97..eac2e22 100644 --- a/src/sql/category.js +++ b/src/sql/category.js @@ -10,7 +10,7 @@ WITH tree AS ( FROM category c INNER JOIN tree t ON c.parent_id = t.id ) -SELECT id, parent_id, sort +SELECT id, parent_id AS parentId, sort FROM tree ORDER BY sort, id `; diff --git a/src/sql/company.js b/src/sql/company.js index 188938b..61c9537 100644 --- a/src/sql/company.js +++ b/src/sql/company.js @@ -43,11 +43,11 @@ WHERE id = @id export const LIST = ` SELECT id, - root_category_id, - default_language_id AS language_id, - taxrate AS taxrate_id, - reducedtaxrate AS reducedtaxrate_id, - taxfreerate AS taxfreerate_id + root_category_id AS rootCategoryId, + default_language_id AS languageId, + taxrate AS taxrateId, + reducedtaxrate AS reducedtaxrateId, + taxfreerate AS taxfreerateId FROM company `; diff --git a/src/sql/google_auth.js b/src/sql/google_auth.js index b63e991..53adbac 100644 --- a/src/sql/google_auth.js +++ b/src/sql/google_auth.js @@ -4,27 +4,21 @@ OUTPUT INSERTED.id VALUES (@email) `; -export const GET_BY_ID = ` -SELECT id, email, created_at, selected_company -FROM google_auth -WHERE id = @id -`; - export const GET_BY_EMAIL = ` -SELECT id, email, created_at, selected_company +SELECT id, email, created_at AS createdAt, selected_company AS selectedCompanyId FROM google_auth WHERE email = @email `; -export const LIST = ` -SELECT id, email, created_at, selected_company +export const LIST_ALLOWED_EMAILS = ` +SELECT id, email, created_at AS createdAt, selected_company AS selectedCompanyId FROM google_auth -ORDER BY id +ORDER BY email `; -export const DEL = ` +export const REMOVE_ALLOWED_EMAIL = ` DELETE FROM google_auth -WHERE id = @id +WHERE email = @email `; export const SET_SELECTED_COMPANY = ` diff --git a/src/store/category/create.js b/src/store/category/create.js index 2a0c748..64794ec 100644 --- a/src/store/category/create.js +++ b/src/store/category/create.js @@ -2,10 +2,10 @@ import sql from 'mssql'; import { CREATE } from '../../sql/category.js'; export default function create(pool) { - return async ({ parent_id, sort }) => { + return async ({ parentId, sort }) => { const result = await pool .request() - .input('parentId', sql.Int, parent_id) + .input('parentId', sql.Int, parentId) .input('sort', sql.Int, sort) .query(CREATE); diff --git a/src/store/company/create.js b/src/store/company/create.js index 7f46975..b7063bb 100644 --- a/src/store/company/create.js +++ b/src/store/company/create.js @@ -46,11 +46,11 @@ export default function create(pool) { return { id: result.recordset[0].id, - root_category_id: rootCategoryId, - language_id: languageId, - taxrate_id: taxrateId, - reducedtaxrate_id: reducedtaxrateId, - taxfreerate_id: taxfreerateId, + rootCategoryId, + languageId, + taxrateId, + reducedtaxrateId, + taxfreerateId, }; } catch (err) { try { diff --git a/src/store/company/update.js b/src/store/company/update.js index c39b9b5..5d3fee0 100644 --- a/src/store/company/update.js +++ b/src/store/company/update.js @@ -45,11 +45,11 @@ export default function update(pool) { return { id, - root_category_id: company.root_category_id, - language_id: languageId, - taxrate_id: taxrateId, - reducedtaxrate_id: reducedtaxrateId, - taxfreerate_id: company.taxfreerate, + rootCategoryId: company.root_category_id, + languageId, + taxrateId, + reducedtaxrateId, + taxfreerateId: company.taxfreerate, }; } catch (err) { try { diff --git a/src/store/google_auth/del.js b/src/store/google_auth/del.js deleted file mode 100644 index 9bf3f79..0000000 --- a/src/store/google_auth/del.js +++ /dev/null @@ -1,11 +0,0 @@ -import sql from 'mssql'; -import { DEL } from '../../sql/google_auth.js'; - -export default function del(pool) { - return async (id) => { - await pool - .request() - .input('id', sql.Int, id) - .query(DEL); - }; -} diff --git a/src/store/google_auth/getById.js b/src/store/google_auth/getById.js deleted file mode 100644 index 96c6fac..0000000 --- a/src/store/google_auth/getById.js +++ /dev/null @@ -1,13 +0,0 @@ -import sql from 'mssql'; -import { GET_BY_ID } from '../../sql/google_auth.js'; - -export default function getById(pool) { - return async (id) => { - const result = await pool - .request() - .input('id', sql.Int, id) - .query(GET_BY_ID); - - return result.recordset[0]; - }; -} diff --git a/src/store/google_auth/list.js b/src/store/google_auth/list.js deleted file mode 100644 index 7f9e765..0000000 --- a/src/store/google_auth/list.js +++ /dev/null @@ -1,8 +0,0 @@ -import { LIST } from '../../sql/google_auth.js'; - -export default function list(pool) { - return async () => { - const result = await pool.request().query(LIST); - return result.recordset; - }; -} diff --git a/src/store/google_auth/listAllowedEmails.js b/src/store/google_auth/listAllowedEmails.js new file mode 100644 index 0000000..34bd0fe --- /dev/null +++ b/src/store/google_auth/listAllowedEmails.js @@ -0,0 +1,8 @@ +import { LIST_ALLOWED_EMAILS } from '../../sql/google_auth.js'; + +export default function listAllowedEmails(pool) { + return async () => { + const result = await pool.request().query(LIST_ALLOWED_EMAILS); + return result.recordset; + }; +} diff --git a/src/store/google_auth/removeAllowedEmail.js b/src/store/google_auth/removeAllowedEmail.js new file mode 100644 index 0000000..f40baf7 --- /dev/null +++ b/src/store/google_auth/removeAllowedEmail.js @@ -0,0 +1,11 @@ +import sql from 'mssql'; +import { REMOVE_ALLOWED_EMAIL } from '../../sql/google_auth.js'; + +export default function removeAllowedEmail(pool) { + return async (email) => { + await pool + .request() + .input('email', sql.NVarChar(320), email) + .query(REMOVE_ALLOWED_EMAIL); + }; +}