piper-utils 1.1.62 → 1.1.64
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +102 -0
- package/README.md +846 -759
- package/bin/main.js +2 -2
- package/bin/main.js.map +1 -1
- package/package.json +1 -1
- package/src/requestResponse/requestResponse.js +11 -3
- package/src/requestResponse/requestResponse.test.js +30 -2
- package/WHITE_LABEL.md +0 -60
|
@@ -81,9 +81,8 @@ export function getCurrentUser(event) {
|
|
|
81
81
|
_.get(event, 'requestContext.authorizer.custom:UID') ||
|
|
82
82
|
'0';
|
|
83
83
|
|
|
84
|
-
const id = JSON.parse(jsonToParse);
|
|
85
|
-
|
|
86
84
|
const username = _.get(event, 'requestContext.authorizer.claims.email') || _.get(event, 'requestContext.authorizer.email') || 'localtestuser@gexample.com';
|
|
85
|
+
const id = process.env.BUILD_ENV === 'local' ? 1 : JSON.parse(jsonToParse);
|
|
87
86
|
|
|
88
87
|
return {
|
|
89
88
|
username,
|
|
@@ -213,7 +212,16 @@ export function detectSequelizeError(body) {
|
|
|
213
212
|
const errorName = _.get(body, 'name', '');
|
|
214
213
|
|
|
215
214
|
if (errorName === 'SequelizeForeignKeyConstraintError') {
|
|
216
|
-
|
|
215
|
+
const detail = _.get(body, 'parent.detail') || _.get(body, 'original.detail') || '';
|
|
216
|
+
const notPresent = detail.match(/Key \((\w+)\)=\((.+?)\) is not present in table "(\w+)"/);
|
|
217
|
+
const stillReferenced = detail.match(/Key \((\w+)\)=\((.+?)\) is still referenced from table "(\w+)"/);
|
|
218
|
+
if (notPresent) {
|
|
219
|
+
errorBody.message = 'The referenced ' + notPresent[3] + ' does not exist (' + notPresent[1] + ': ' + notPresent[2] + ')';
|
|
220
|
+
} else if (stillReferenced) {
|
|
221
|
+
errorBody.message = 'This item cannot be removed because it is referenced by ' + stillReferenced[3];
|
|
222
|
+
} else {
|
|
223
|
+
errorBody.message = 'A referenced item does not exist or is still in use';
|
|
224
|
+
}
|
|
217
225
|
errorBody.statusCode = 409;
|
|
218
226
|
errorBody.errorCode = '4090';
|
|
219
227
|
return errorBody;
|
|
@@ -230,7 +230,22 @@ describe('requestResponse', () => {
|
|
|
230
230
|
expect(res).toEqual({ message: 'Ilikegreeneggs' });
|
|
231
231
|
});
|
|
232
232
|
|
|
233
|
-
it('should return
|
|
233
|
+
it('should return user-friendly message when referenced record does not exist', () => {
|
|
234
|
+
const body = {
|
|
235
|
+
name: 'SequelizeForeignKeyConstraintError',
|
|
236
|
+
parent: { detail: 'Key (partId)=(54) is not present in table "part".' },
|
|
237
|
+
original: { detail: 'Key (partId)=(54) is not present in table "part".' }
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
const res = detectSequelizeError(body);
|
|
241
|
+
expect(res).toEqual({
|
|
242
|
+
message: 'The referenced part does not exist (partId: 54)',
|
|
243
|
+
statusCode: 409,
|
|
244
|
+
errorCode: '4090'
|
|
245
|
+
});
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
it('should return user-friendly message when record is still referenced', () => {
|
|
234
249
|
const body = {
|
|
235
250
|
name: 'SequelizeForeignKeyConstraintError',
|
|
236
251
|
parent: { detail: 'Key (id)=(5) is still referenced from table "orders".' },
|
|
@@ -239,7 +254,20 @@ describe('requestResponse', () => {
|
|
|
239
254
|
|
|
240
255
|
const res = detectSequelizeError(body);
|
|
241
256
|
expect(res).toEqual({
|
|
242
|
-
message: '
|
|
257
|
+
message: 'This item cannot be removed because it is referenced by orders',
|
|
258
|
+
statusCode: 409,
|
|
259
|
+
errorCode: '4090'
|
|
260
|
+
});
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
it('should return generic message for foreign key constraint errors with no detail', () => {
|
|
264
|
+
const body = {
|
|
265
|
+
name: 'SequelizeForeignKeyConstraintError'
|
|
266
|
+
};
|
|
267
|
+
|
|
268
|
+
const res = detectSequelizeError(body);
|
|
269
|
+
expect(res).toEqual({
|
|
270
|
+
message: 'A referenced item does not exist or is still in use',
|
|
243
271
|
statusCode: 409,
|
|
244
272
|
errorCode: '4090'
|
|
245
273
|
});
|
package/WHITE_LABEL.md
DELETED
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
# White Label Partner System — piper-utils
|
|
2
|
-
|
|
3
|
-
## Overview
|
|
4
|
-
|
|
5
|
-
This repo provides shared utility functions for identifying partner users and enriching their access rights. These functions are used by piper (ERP) and can be used by any service that processes Lambda events with JWT claims.
|
|
6
|
-
|
|
7
|
-
## Files
|
|
8
|
-
|
|
9
|
-
| File | Purpose |
|
|
10
|
-
|------|---------|
|
|
11
|
-
| `src/database/dbUtils/queryStringUtils/accessRightsUtils.js` | Added 4 partner functions |
|
|
12
|
-
| `src/index.js` | Exports the new functions |
|
|
13
|
-
|
|
14
|
-
## JWT Claims
|
|
15
|
-
|
|
16
|
-
Two Cognito custom attributes drive the partner system:
|
|
17
|
-
|
|
18
|
-
| Claim | Meaning | Set on |
|
|
19
|
-
|-------|---------|--------|
|
|
20
|
-
| `custom:PID` | User is a partner admin | Partner admin users |
|
|
21
|
-
| `custom:BPID` | User's business belongs to a partner | Merchant users managed by a partner |
|
|
22
|
-
|
|
23
|
-
## Functions
|
|
24
|
-
|
|
25
|
-
### `isPartnerUser(event)`
|
|
26
|
-
Returns the partner ID from `custom:PID` if present, otherwise `false`. Use this to identify partner admin users who manage multiple businesses.
|
|
27
|
-
|
|
28
|
-
### `getBelongsToPartnerId(event)`
|
|
29
|
-
Returns the partner ID from `custom:BPID` if present, otherwise `false`. Use this to identify merchant users whose business is managed by a partner (branding only, no extra access).
|
|
30
|
-
|
|
31
|
-
### `getEffectivePartnerId(event)`
|
|
32
|
-
Returns whichever partner ID exists — checks `custom:PID` first, falls back to `custom:BPID`. Used by the whoAmI endpoint to look up branding regardless of user type.
|
|
33
|
-
|
|
34
|
-
### `enrichEventWithPartnerAccess(event, partnerBusinessIds, role = 'R')`
|
|
35
|
-
Mutates the event's `custom:AR` claim in-memory to include the partner's business IDs. This is the "enrichment pattern" — the caller does the DB lookup to get the business IDs, then calls this function before calling any existing access control functions like `accessRightsUtils()` or `checkWriteAccess()`.
|
|
36
|
-
|
|
37
|
-
**Why mutate instead of making accessRightsUtils async?** The access rights functions are synchronous and used across every service. Making them async would be a massive breaking change. The enrichment pattern keeps them synchronous — the caller does the async DB work, enriches the event, then the existing sync functions work as-is.
|
|
38
|
-
|
|
39
|
-
## Example Usage
|
|
40
|
-
|
|
41
|
-
```javascript
|
|
42
|
-
import { isPartnerUser, enrichEventWithPartnerAccess } from 'piper-utils';
|
|
43
|
-
|
|
44
|
-
// In a route handler:
|
|
45
|
-
const partnerId = isPartnerUser(event);
|
|
46
|
-
if (partnerId) {
|
|
47
|
-
// Fetch partner's business IDs from DynamoDB
|
|
48
|
-
const partner = await getPartnerById(partnerId);
|
|
49
|
-
// Enrich the event so accessRightsUtils sees the partner's businesses
|
|
50
|
-
enrichEventWithPartnerAccess(event, partner.businessIds, 'R');
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
// Now existing access control works as normal
|
|
54
|
-
const businessIds = accessRightsUtils(event);
|
|
55
|
-
// businessIds now includes the partner's businesses with 'R' access
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
## Build
|
|
59
|
-
|
|
60
|
-
After modifying source files, rebuild with `npm run build`. The compiled output goes to `bin/main.js`. Other repos consume piper-utils via npm, so they need `npm install` or `npm link` to pick up changes.
|