diff --git a/README.md b/README.md index 00c568c..8eb7336 100644 --- a/README.md +++ b/README.md @@ -120,8 +120,7 @@ Backend mapping: | Method | Signature | Returns | | --- | --- | --- | | `createUnderParent` | `({ googleAuthId, parentId, name })` | `{ id, parentId, sort, name }` | -| `updateParentId` | `(id, parentId)` | `void` | -| `delete` | `(id)` | `void` — blocked by FK if `categoryarticle` (or child categories) still reference it | +| `delete` | `({ googleAuthId, id })` | `void` — blocked by FK if `categoryarticle` or child categories remain | | `getTreeForCompany` | `(googleAuthId, companyId)` | `{ languageId, tree, articles }` | ### `createUnderParent` (transaction) @@ -131,6 +130,8 @@ Backend mapping: 3. `companyuser` for `(googleAuthId, companyId)`, else `Not authorized` 4. Insert `category` (next sibling `sort`) + `categorytext` (`name`, slugified `slug`, company default language) +All category mutations use `resolveCategoryAccess(googleAuthId, categoryId)` internally. Root category cannot be deleted. + `getTreeForCompany` throws `Not authorized` if `googleAuthId` is not a `companyuser` of `companyId`, and `Company not found` if the company is missing, has no `root_category_id`, or no `default_language_id`. --- @@ -146,6 +147,7 @@ Backend mapping: | `PUT /api/auth/selected-company` | `google_auth.setSelectedCompany(googleAuthId, companyId)` | | `GET /api/category/tree?companyId=` | `category.getTreeForCompany(googleAuthId, companyId)` | | `POST /api/category` | `category.createUnderParent({ googleAuthId, parentId, name })` | +| `DELETE /api/category/:id` | `category.delete({ googleAuthId, id })` | --- diff --git a/scripts/test-api.js b/scripts/test-api.js index 8fce9f2..23d470e 100644 --- a/scripts/test-api.js +++ b/scripts/test-api.js @@ -96,8 +96,7 @@ const MENU = { label: 'category', items: [ { value: 'category.createUnderParent', label: 'createUnderParent', hint: 'googleAuthId, parentId, name → row' }, - { value: 'category.updateParentId', label: 'updateParentId', hint: 'id, parentId' }, - { value: 'category.delete', label: 'delete', hint: 'id' }, + { value: 'category.delete', label: 'delete', hint: 'googleAuthId, id' }, { value: 'category.getTreeForCompany', label: 'getTreeForCompany', hint: 'googleAuthId, companyId → rows' }, ], }, @@ -320,23 +319,15 @@ async function runMethod(client, action) { }), ]; } - case 'category.updateParentId': { - const id = await askInt('Category id'); - if (id == null) return; - const parentRaw = await askText('parentId (null as empty)', { - placeholder: '', - validate: (v) => - v?.trim() && !/^\d+$/.test(v.trim()) ? 'Integer or empty' : undefined, - }); - if (parentRaw == null) return; - const parentId = parentRaw === '' ? null : Number(parentRaw); - await client.category.updateParentId(Number(id), parentId); - return ['ok', true]; - } case 'category.delete': { + const googleAuthId = await askInt('google_auth id'); + if (googleAuthId == null) return; const id = await askInt('Category id'); if (id == null) return; - await client.category.delete(Number(id)); + await client.category.delete({ + googleAuthId: Number(googleAuthId), + id: Number(id), + }); return ['ok', true]; } case 'category.getTreeForCompany': { diff --git a/src/index.js b/src/index.js index 0fbbc9c..2ecce9e 100644 --- a/src/index.js +++ b/src/index.js @@ -7,7 +7,6 @@ import companyDelete from './store/company/delete.js'; import companyList from './store/company/list.js'; import categoryGetTreeForCompany from './store/category/getTreeForCompany.js'; import categoryCreateUnderParent from './store/category/createUnderParent.js'; -import categoryUpdateParentId from './store/category/updateParentId.js'; import categoryDelete from './store/category/delete.js'; import langCreate from './store/lang/create.js'; import langDelete from './store/lang/delete.js'; @@ -46,7 +45,6 @@ export default class LibDashStore { this.category = { getTreeForCompany: categoryGetTreeForCompany(pool), createUnderParent: categoryCreateUnderParent(pool), - updateParentId: categoryUpdateParentId(pool), delete: categoryDelete(pool), }; diff --git a/src/sql/category.js b/src/sql/category.js index 580a602..93cfd31 100644 --- a/src/sql/category.js +++ b/src/sql/category.js @@ -126,12 +126,6 @@ FROM category WHERE parent_id = @parentId `; -export const UPDATE_PARENT_ID = ` -UPDATE category -SET parent_id = @parentId -WHERE id = @id -`; - export const DELETE = ` DELETE FROM category WHERE id = @id diff --git a/src/store/category/delete.js b/src/store/category/delete.js index 7a48ec9..8f85947 100644 --- a/src/store/category/delete.js +++ b/src/store/category/delete.js @@ -1,7 +1,16 @@ import { DELETE } from '../../sql/category.js'; +import resolveCategoryAccess from './resolveCategoryAccess.js'; export default function del(pool) { - return async (id) => { + const access = resolveCategoryAccess(pool); + + return async ({ googleAuthId, id }) => { + const { rootCategoryId } = await access(googleAuthId, id); + + if (id === rootCategoryId) { + throw new Error('Cannot delete root category'); + } + await pool.request().input('id', id).query(DELETE); }; } diff --git a/src/store/category/updateParentId.js b/src/store/category/updateParentId.js deleted file mode 100644 index 25e720a..0000000 --- a/src/store/category/updateParentId.js +++ /dev/null @@ -1,11 +0,0 @@ -import { UPDATE_PARENT_ID } from '../../sql/category.js'; - -export default function updateParentId(pool) { - return async (id, parentId) => { - await pool - .request() - .input('id', id) - .input('parentId', parentId) - .query(UPDATE_PARENT_ID); - }; -}