@volontariapp/storage-client 0.2.2 → 0.3.0

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.
Files changed (29) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/dist/errors/grpc-error.d.ts +3 -0
  3. package/dist/errors/grpc-error.d.ts.map +1 -0
  4. package/dist/errors/grpc-error.js +9 -0
  5. package/dist/errors/grpc-error.js.map +1 -0
  6. package/dist/errors/index.d.ts +4 -2
  7. package/dist/errors/index.d.ts.map +1 -1
  8. package/dist/errors/index.js +4 -8
  9. package/dist/errors/index.js.map +1 -1
  10. package/dist/errors/is-storage-unavailable.d.ts +2 -0
  11. package/dist/errors/is-storage-unavailable.d.ts.map +1 -0
  12. package/dist/errors/is-storage-unavailable.js +21 -0
  13. package/dist/errors/is-storage-unavailable.js.map +1 -0
  14. package/dist/errors/storage-relayed.error.d.ts +15 -0
  15. package/dist/errors/storage-relayed.error.d.ts.map +1 -0
  16. package/dist/errors/storage-relayed.error.js +38 -0
  17. package/dist/errors/storage-relayed.error.js.map +1 -0
  18. package/dist/errors/storage-unavailable.error.d.ts +7 -0
  19. package/dist/errors/storage-unavailable.error.d.ts.map +1 -0
  20. package/dist/errors/storage-unavailable.error.js +11 -0
  21. package/dist/errors/storage-unavailable.error.js.map +1 -0
  22. package/dist/services/storage-client.service.d.ts.map +1 -1
  23. package/dist/services/storage-client.service.js +12 -3
  24. package/dist/services/storage-client.service.js.map +1 -1
  25. package/dist/test/storage-client-errors.unit.spec.d.ts +2 -0
  26. package/dist/test/storage-client-errors.unit.spec.d.ts.map +1 -0
  27. package/dist/test/storage-client-errors.unit.spec.js +124 -0
  28. package/dist/test/storage-client-errors.unit.spec.js.map +1 -0
  29. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.3.0
4
+
5
+ ### Minor Changes
6
+
7
+ - Relay the business errors of ms-storage (file not found, attachment refused...) with their own status and code instead of wrapping them in STORAGE_ATTACHMENT_FAILED (500). Transport failures keep their existing codes, so the asynchronous fallback of ms-post and ms-event is unchanged. The reason of STORAGE_ATTACHMENT_FAILED now starts with the gRPC status name (for example "UNAVAILABLE: ..."). Add the isStorageUnavailable helper and the StorageUnavailableError (503) for consumers without an asynchronous fallback, such as the avatar of ms-user.
8
+
3
9
  ## 0.2.2
4
10
 
5
11
  ### Patch Changes
@@ -0,0 +1,3 @@
1
+ import type { GrpcStatusError } from '../interfaces/index.js';
2
+ export declare function isGrpcError(error: unknown): error is GrpcStatusError;
3
+ //# sourceMappingURL=grpc-error.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"grpc-error.d.ts","sourceRoot":"","sources":["../../src/errors/grpc-error.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAE9D,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,eAAe,CASpE"}
@@ -0,0 +1,9 @@
1
+ export function isGrpcError(error) {
2
+ return (typeof error === 'object' &&
3
+ error !== null &&
4
+ 'code' in error &&
5
+ typeof error.code === 'number' &&
6
+ 'details' in error &&
7
+ typeof error.details === 'string');
8
+ }
9
+ //# sourceMappingURL=grpc-error.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"grpc-error.js","sourceRoot":"","sources":["../../src/errors/grpc-error.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,WAAW,CAAC,KAAc;IACxC,OAAO,CACL,OAAO,KAAK,KAAK,QAAQ;QACzB,KAAK,KAAK,IAAI;QACd,MAAM,IAAI,KAAK;QACf,OAAQ,KAA2B,CAAC,IAAI,KAAK,QAAQ;QACrD,SAAS,IAAI,KAAK;QAClB,OAAQ,KAA8B,CAAC,OAAO,KAAK,QAAQ,CAC5D,CAAC;AACJ,CAAC"}
@@ -6,6 +6,8 @@ export declare class StorageClientError extends Error {
6
6
  export declare class StorageClientTimeoutError extends StorageClientError {
7
7
  constructor(message?: string);
8
8
  }
9
- import type { GrpcStatusError } from '../interfaces/index.js';
10
- export declare function isGrpcError(error: unknown): error is GrpcStatusError;
9
+ export * from './storage-unavailable.error.js';
10
+ export * from './storage-relayed.error.js';
11
+ export * from './is-storage-unavailable.js';
12
+ export * from './grpc-error.js';
11
13
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/errors/index.ts"],"names":[],"mappings":"AAAA,qBAAa,kBAAmB,SAAQ,KAAK;aAGzB,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM;aACtB,OAAO,CAAC,EAAE,OAAO;gBAFjC,OAAO,EAAE,MAAM,EACC,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,YAAA,EACtB,OAAO,CAAC,EAAE,OAAO,YAAA;CAMpC;AAED,qBAAa,yBAA0B,SAAQ,kBAAkB;gBACnD,OAAO,SAAgC;CAKpD;AAED,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAE9D,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,eAAe,CASpE"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/errors/index.ts"],"names":[],"mappings":"AAAA,qBAAa,kBAAmB,SAAQ,KAAK;aAGzB,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM;aACtB,OAAO,CAAC,EAAE,OAAO;gBAFjC,OAAO,EAAE,MAAM,EACC,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,YAAA,EACtB,OAAO,CAAC,EAAE,OAAO,YAAA;CAMpC;AAED,qBAAa,yBAA0B,SAAQ,kBAAkB;gBACnD,OAAO,SAAgC;CAKpD;AAED,cAAc,gCAAgC,CAAC;AAC/C,cAAc,4BAA4B,CAAC;AAC3C,cAAc,6BAA6B,CAAC;AAC5C,cAAc,iBAAiB,CAAC"}
@@ -16,12 +16,8 @@ export class StorageClientTimeoutError extends StorageClientError {
16
16
  Object.setPrototypeOf(this, new.target.prototype);
17
17
  }
18
18
  }
19
- export function isGrpcError(error) {
20
- return (typeof error === 'object' &&
21
- error !== null &&
22
- 'code' in error &&
23
- typeof error.code === 'number' &&
24
- 'details' in error &&
25
- typeof error.details === 'string');
26
- }
19
+ export * from './storage-unavailable.error.js';
20
+ export * from './storage-relayed.error.js';
21
+ export * from './is-storage-unavailable.js';
22
+ export * from './grpc-error.js';
27
23
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/errors/index.ts"],"names":[],"mappings":"AAAA,MAAM,OAAO,kBAAmB,SAAQ,KAAK;IAGzB;IACA;IAHlB,YACE,OAAe,EACC,IAAsB,EACtB,OAAiB;QAEjC,KAAK,CAAC,OAAO,CAAC,CAAC;QAHC,SAAI,GAAJ,IAAI,CAAkB;QACtB,YAAO,GAAP,OAAO,CAAU;QAGjC,IAAI,CAAC,IAAI,GAAG,oBAAoB,CAAC;QACjC,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IACpD,CAAC;CACF;AAED,MAAM,OAAO,yBAA0B,SAAQ,kBAAkB;IAC/D,YAAY,OAAO,GAAG,6BAA6B;QACjD,KAAK,CAAC,OAAO,EAAE,mBAAmB,CAAC,CAAC;QACpC,IAAI,CAAC,IAAI,GAAG,2BAA2B,CAAC;QACxC,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IACpD,CAAC;CACF;AAID,MAAM,UAAU,WAAW,CAAC,KAAc;IACxC,OAAO,CACL,OAAO,KAAK,KAAK,QAAQ;QACzB,KAAK,KAAK,IAAI;QACd,MAAM,IAAI,KAAK;QACf,OAAQ,KAA2B,CAAC,IAAI,KAAK,QAAQ;QACrD,SAAS,IAAI,KAAK;QAClB,OAAQ,KAA8B,CAAC,OAAO,KAAK,QAAQ,CAC5D,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/errors/index.ts"],"names":[],"mappings":"AAAA,MAAM,OAAO,kBAAmB,SAAQ,KAAK;IAGzB;IACA;IAHlB,YACE,OAAe,EACC,IAAsB,EACtB,OAAiB;QAEjC,KAAK,CAAC,OAAO,CAAC,CAAC;QAHC,SAAI,GAAJ,IAAI,CAAkB;QACtB,YAAO,GAAP,OAAO,CAAU;QAGjC,IAAI,CAAC,IAAI,GAAG,oBAAoB,CAAC;QACjC,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IACpD,CAAC;CACF;AAED,MAAM,OAAO,yBAA0B,SAAQ,kBAAkB;IAC/D,YAAY,OAAO,GAAG,6BAA6B;QACjD,KAAK,CAAC,OAAO,EAAE,mBAAmB,CAAC,CAAC;QACpC,IAAI,CAAC,IAAI,GAAG,2BAA2B,CAAC;QACxC,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IACpD,CAAC;CACF;AAED,cAAc,gCAAgC,CAAC;AAC/C,cAAc,4BAA4B,CAAC;AAC3C,cAAc,6BAA6B,CAAC;AAC5C,cAAc,iBAAiB,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare const isStorageUnavailable: (error: unknown) => boolean;
2
+ //# sourceMappingURL=is-storage-unavailable.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"is-storage-unavailable.d.ts","sourceRoot":"","sources":["../../src/errors/is-storage-unavailable.ts"],"names":[],"mappings":"AAUA,eAAO,MAAM,oBAAoB,UAAW,OAAO,KAAG,OAmBrD,CAAC"}
@@ -0,0 +1,21 @@
1
+ import { isBaseError } from '@volontariapp/errors';
2
+ import { isGrpcError } from './grpc-error.js';
3
+ const GRPC_UNAVAILABLE = 14;
4
+ const GRPC_DEADLINE_EXCEEDED = 4;
5
+ export const isStorageUnavailable = (error) => {
6
+ if (isBaseError(error)) {
7
+ if (error.code === 'STORAGE_TIMEOUT') {
8
+ return true;
9
+ }
10
+ if (error.code === 'STORAGE_ATTACHMENT_FAILED') {
11
+ const rawReason = error.details?.['reason'];
12
+ const reason = typeof rawReason === 'string' ? rawReason.toUpperCase() : '';
13
+ return (reason.includes('UNAVAILABLE') ||
14
+ reason.includes('DEADLINE_EXCEEDED') ||
15
+ reason.includes('CONNECT ECONNREFUSED'));
16
+ }
17
+ return false;
18
+ }
19
+ return (isGrpcError(error) && (error.code === GRPC_UNAVAILABLE || error.code === GRPC_DEADLINE_EXCEEDED));
20
+ };
21
+ //# sourceMappingURL=is-storage-unavailable.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"is-storage-unavailable.js","sourceRoot":"","sources":["../../src/errors/is-storage-unavailable.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACnD,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAE9C,MAAM,gBAAgB,GAAG,EAAE,CAAC;AAC5B,MAAM,sBAAsB,GAAG,CAAC,CAAC;AAMjC,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,KAAc,EAAW,EAAE;IAC9D,IAAI,WAAW,CAAC,KAAK,CAAC,EAAE,CAAC;QACvB,IAAI,KAAK,CAAC,IAAI,KAAK,iBAAiB,EAAE,CAAC;YACrC,OAAO,IAAI,CAAC;QACd,CAAC;QACD,IAAI,KAAK,CAAC,IAAI,KAAK,2BAA2B,EAAE,CAAC;YAC/C,MAAM,SAAS,GAAG,KAAK,CAAC,OAAO,EAAE,CAAC,QAAQ,CAAC,CAAC;YAC5C,MAAM,MAAM,GAAG,OAAO,SAAS,KAAK,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAC5E,OAAO,CACL,MAAM,CAAC,QAAQ,CAAC,aAAa,CAAC;gBAC9B,MAAM,CAAC,QAAQ,CAAC,mBAAmB,CAAC;gBACpC,MAAM,CAAC,QAAQ,CAAC,sBAAsB,CAAC,CACxC,CAAC;QACJ,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;IACD,OAAO,CACL,WAAW,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,KAAK,gBAAgB,IAAI,KAAK,CAAC,IAAI,KAAK,sBAAsB,CAAC,CACjG,CAAC;AACJ,CAAC,CAAC"}
@@ -0,0 +1,15 @@
1
+ import { BaseApiError, type GrpcStatus } from '@volontariapp/errors';
2
+ import type { GrpcStatusError } from '../interfaces/index.js';
3
+ export interface StorageErrorBody {
4
+ statusCode: number;
5
+ code: string;
6
+ message: string;
7
+ details?: Record<string, unknown>;
8
+ }
9
+ export declare class StorageRelayedError extends BaseApiError {
10
+ readonly statusCode: number;
11
+ readonly grpcCode: GrpcStatus;
12
+ constructor(body: StorageErrorBody, grpcCode: GrpcStatus);
13
+ }
14
+ export declare const relayStorageError: (error: GrpcStatusError) => StorageRelayedError | undefined;
15
+ //# sourceMappingURL=storage-relayed.error.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"storage-relayed.error.d.ts","sourceRoot":"","sources":["../../src/errors/storage-relayed.error.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,KAAK,UAAU,EAAE,MAAM,sBAAsB,CAAC;AACrE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAG9D,MAAM,WAAW,gBAAgB;IAC/B,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACnC;AAiCD,qBAAa,mBAAoB,SAAQ,YAAY;IACnD,SAAgB,UAAU,EAAE,MAAM,CAAC;IACnC,SAAgB,QAAQ,EAAE,UAAU,CAAC;gBAEzB,IAAI,EAAE,gBAAgB,EAAE,QAAQ,EAAE,UAAU;CAKzD;AAGD,eAAO,MAAM,iBAAiB,UAAW,eAAe,KAAG,mBAAmB,GAAG,SAGhF,CAAC"}
@@ -0,0 +1,38 @@
1
+ import { BaseApiError } from '@volontariapp/errors';
2
+ const isRecord = (value) => typeof value === 'object' && value !== null && !Array.isArray(value);
3
+ const parseBody = (raw) => {
4
+ let parsed;
5
+ try {
6
+ parsed = JSON.parse(raw);
7
+ }
8
+ catch {
9
+ return undefined;
10
+ }
11
+ if (!isRecord(parsed) ||
12
+ typeof parsed['statusCode'] !== 'number' ||
13
+ typeof parsed['code'] !== 'string' ||
14
+ typeof parsed['message'] !== 'string') {
15
+ return undefined;
16
+ }
17
+ const details = parsed['details'];
18
+ return {
19
+ statusCode: parsed['statusCode'],
20
+ code: parsed['code'],
21
+ message: parsed['message'],
22
+ ...(isRecord(details) ? { details } : {}),
23
+ };
24
+ };
25
+ export class StorageRelayedError extends BaseApiError {
26
+ statusCode;
27
+ grpcCode;
28
+ constructor(body, grpcCode) {
29
+ super(body.message, body.code, body.details);
30
+ this.statusCode = body.statusCode;
31
+ this.grpcCode = grpcCode;
32
+ }
33
+ }
34
+ export const relayStorageError = (error) => {
35
+ const body = parseBody(error.details);
36
+ return body === undefined ? undefined : new StorageRelayedError(body, error.code);
37
+ };
38
+ //# sourceMappingURL=storage-relayed.error.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"storage-relayed.error.js","sourceRoot":"","sources":["../../src/errors/storage-relayed.error.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAmB,MAAM,sBAAsB,CAAC;AAWrE,MAAM,QAAQ,GAAG,CAAC,KAAc,EAAoC,EAAE,CACpE,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;AAEvE,MAAM,SAAS,GAAG,CAAC,GAAW,EAAgC,EAAE;IAC9D,IAAI,MAAe,CAAC;IACpB,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC3B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,IACE,CAAC,QAAQ,CAAC,MAAM,CAAC;QACjB,OAAO,MAAM,CAAC,YAAY,CAAC,KAAK,QAAQ;QACxC,OAAO,MAAM,CAAC,MAAM,CAAC,KAAK,QAAQ;QAClC,OAAO,MAAM,CAAC,SAAS,CAAC,KAAK,QAAQ,EACrC,CAAC;QACD,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,MAAM,OAAO,GAAG,MAAM,CAAC,SAAS,CAAC,CAAC;IAClC,OAAO;QACL,UAAU,EAAE,MAAM,CAAC,YAAY,CAAC;QAChC,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC;QACpB,OAAO,EAAE,MAAM,CAAC,SAAS,CAAC;QAC1B,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAC1C,CAAC;AACJ,CAAC,CAAC;AAMF,MAAM,OAAO,mBAAoB,SAAQ,YAAY;IACnC,UAAU,CAAS;IACnB,QAAQ,CAAa;IAErC,YAAY,IAAsB,EAAE,QAAoB;QACtD,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;QAC7C,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,UAAU,CAAC;QAClC,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;IAC3B,CAAC;CACF;AAGD,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,KAAsB,EAAmC,EAAE;IAC3F,MAAM,IAAI,GAAG,SAAS,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IACtC,OAAO,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,mBAAmB,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;AACpF,CAAC,CAAC"}
@@ -0,0 +1,7 @@
1
+ import { BaseApiError, GrpcStatus } from '@volontariapp/errors';
2
+ export declare class StorageUnavailableError extends BaseApiError {
3
+ readonly statusCode = 503;
4
+ readonly grpcCode = GrpcStatus.UNAVAILABLE;
5
+ constructor(entityId: string);
6
+ }
7
+ //# sourceMappingURL=storage-unavailable.error.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"storage-unavailable.error.d.ts","sourceRoot":"","sources":["../../src/errors/storage-unavailable.error.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAMhE,qBAAa,uBAAwB,SAAQ,YAAY;IACvD,SAAgB,UAAU,OAAO;IACjC,SAAgB,QAAQ,0BAA0B;gBAEtC,QAAQ,EAAE,MAAM;CAK7B"}
@@ -0,0 +1,11 @@
1
+ import { BaseApiError, GrpcStatus } from '@volontariapp/errors';
2
+ export class StorageUnavailableError extends BaseApiError {
3
+ statusCode = 503;
4
+ grpcCode = GrpcStatus.UNAVAILABLE;
5
+ constructor(entityId) {
6
+ super(`Storage service is unavailable for entity ${entityId}`, 'STORAGE_UNAVAILABLE', {
7
+ entityId,
8
+ });
9
+ }
10
+ }
11
+ //# sourceMappingURL=storage-unavailable.error.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"storage-unavailable.error.js","sourceRoot":"","sources":["../../src/errors/storage-unavailable.error.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAMhE,MAAM,OAAO,uBAAwB,SAAQ,YAAY;IACvC,UAAU,GAAG,GAAG,CAAC;IACjB,QAAQ,GAAG,UAAU,CAAC,WAAW,CAAC;IAElD,YAAY,QAAgB;QAC1B,KAAK,CAAC,6CAA6C,QAAQ,EAAE,EAAE,qBAAqB,EAAE;YACpF,QAAQ;SACT,CAAC,CAAC;IACL,CAAC;CACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"storage-client.service.d.ts","sourceRoot":"","sources":["../../src/services/storage-client.service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsB,YAAY,EAAY,MAAM,gBAAgB,CAAC;AAC5E,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAGxD,OAAO,EAGL,KAAK,4BAA4B,EACjC,KAAK,6BAA6B,EAEnC,MAAM,8BAA8B,CAAC;AAKtC,OAAO,KAAK,EACV,wBAAwB,EACxB,oBAAoB,EAErB,MAAM,wBAAwB,CAAC;AAGhC,qBACa,oBAAqB,YAAW,YAAY;IAMrD,OAAO,CAAC,QAAQ,CAAC,UAAU;IAG3B,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAC;IARlC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAsD;IAC7E,OAAO,CAAC,cAAc,CAAsC;gBAIzC,UAAU,EAAE,UAAU,EAGtB,cAAc,CAAC,EAAE,oBAAoB,YAAA;IAGxD,YAAY;IAKN,qBAAqB,CACzB,OAAO,EAAE,4BAA4B,EACrC,OAAO,CAAC,EAAE,wBAAwB,GACjC,OAAO,CAAC,6BAA6B,CAAC;IA8BnC,qBAAqB,CACzB,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,EAAE,EACjB,OAAO,CAAC,EAAE,wBAAwB,GACjC,OAAO,CAAC,6BAA6B,CAAC;IAWnC,2BAA2B,CAC/B,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,EACd,OAAO,CAAC,EAAE,wBAAwB,GACjC,OAAO,CAAC,6BAA6B,CAAC;IAWnC,2BAA2B,CAC/B,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,OAAO,CAAC,EAAE,wBAAwB,GACjC,OAAO,CAAC,6BAA6B,CAAC;IAWnC,0BAA0B,CAC9B,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,EACd,OAAO,CAAC,EAAE,wBAAwB,GACjC,OAAO,CAAC,6BAA6B,CAAC;CAU1C"}
1
+ {"version":3,"file":"storage-client.service.d.ts","sourceRoot":"","sources":["../../src/services/storage-client.service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsB,YAAY,EAAY,MAAM,gBAAgB,CAAC;AAC5E,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAGxD,OAAO,EAGL,KAAK,4BAA4B,EACjC,KAAK,6BAA6B,EAEnC,MAAM,8BAA8B,CAAC;AAKtC,OAAO,KAAK,EACV,wBAAwB,EACxB,oBAAoB,EAErB,MAAM,wBAAwB,CAAC;AAGhC,qBACa,oBAAqB,YAAW,YAAY;IAMrD,OAAO,CAAC,QAAQ,CAAC,UAAU;IAG3B,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAC;IARlC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAsD;IAC7E,OAAO,CAAC,cAAc,CAAsC;gBAIzC,UAAU,EAAE,UAAU,EAGtB,cAAc,CAAC,EAAE,oBAAoB,YAAA;IAGxD,YAAY;IAKN,qBAAqB,CACzB,OAAO,EAAE,4BAA4B,EACrC,OAAO,CAAC,EAAE,wBAAwB,GACjC,OAAO,CAAC,6BAA6B,CAAC;IA0CnC,qBAAqB,CACzB,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,EAAE,EACjB,OAAO,CAAC,EAAE,wBAAwB,GACjC,OAAO,CAAC,6BAA6B,CAAC;IAWnC,2BAA2B,CAC/B,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,EACd,OAAO,CAAC,EAAE,wBAAwB,GACjC,OAAO,CAAC,6BAA6B,CAAC;IAWnC,2BAA2B,CAC/B,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,OAAO,CAAC,EAAE,wBAAwB,GACjC,OAAO,CAAC,6BAA6B,CAAC;IAWnC,0BAA0B,CAC9B,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,EACd,OAAO,CAAC,EAAE,wBAAwB,GACjC,OAAO,CAAC,6BAA6B,CAAC;CAU1C"}
@@ -16,10 +16,10 @@ import { Metadata } from '@grpc/grpc-js';
16
16
  import { firstValueFrom, throwError, timeout } from 'rxjs';
17
17
  import { STORAGE_SERVICE_NAME, EntityType, } from '@volontariapp/contracts-nest';
18
18
  import { Logger } from '@volontariapp/logger';
19
- import { isBaseError } from '@volontariapp/errors';
19
+ import { GrpcStatus, isBaseError } from '@volontariapp/errors';
20
20
  import { STORAGE_ATTACHMENT_FAILED, STORAGE_TIMEOUT } from '@volontariapp/errors-nest';
21
21
  import { DEFAULT_STORAGE_TIMEOUT_MS, STORAGE_CLIENT_OPTIONS } from '../constants.js';
22
- import { isGrpcError } from '../errors/index.js';
22
+ import { isGrpcError, relayStorageError } from '../errors/index.js';
23
23
  let StorageClientService = StorageClientService_1 = class StorageClientService {
24
24
  clientGrpc;
25
25
  defaultOptions;
@@ -51,7 +51,16 @@ let StorageClientService = StorageClientService_1 = class StorageClientService {
51
51
  throw error;
52
52
  }
53
53
  const grpcErr = isGrpcError(error) ? error : undefined;
54
- const message = grpcErr?.details ?? (error instanceof Error ? error.message : String(error));
54
+ const relayed = grpcErr === undefined ? undefined : relayStorageError(grpcErr);
55
+ if (relayed !== undefined) {
56
+ this.logger.warn(`ConfirmFileAttachment refused for entity ${command.entityId}: ${relayed.code}`);
57
+ throw relayed;
58
+ }
59
+ const message = grpcErr === undefined
60
+ ? error instanceof Error
61
+ ? error.message
62
+ : String(error)
63
+ : `${GrpcStatus[grpcErr.code] ?? String(grpcErr.code)}: ${grpcErr.details}`;
55
64
  this.logger.error(`ConfirmFileAttachment failed for entity ${command.entityId}: ${message}`);
56
65
  throw STORAGE_ATTACHMENT_FAILED(command.entityId, message);
57
66
  }
@@ -1 +1 @@
1
- {"version":3,"file":"storage-client.service.js","sourceRoot":"","sources":["../../src/services/storage-client.service.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"storage-client.service.js","sourceRoot":"","sources":["../../src/services/storage-client.service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import 'reflect-metadata';
2
+ //# sourceMappingURL=storage-client-errors.unit.spec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"storage-client-errors.unit.spec.d.ts","sourceRoot":"","sources":["../../src/test/storage-client-errors.unit.spec.ts"],"names":[],"mappings":"AAAA,OAAO,kBAAkB,CAAC"}
@@ -0,0 +1,124 @@
1
+ import 'reflect-metadata';
2
+ import { jest, describe, it, expect, beforeEach } from '@jest/globals';
3
+ import { throwError } from 'rxjs';
4
+ import { GrpcStatus, isBaseApiError } from '@volontariapp/errors';
5
+ import { EntityType, STORAGE_ATTACHMENT_FAILED, STORAGE_TIMEOUT, StorageClientService, StorageUnavailableError, isStorageUnavailable, } from '../index.js';
6
+ const command = {
7
+ entityId: 'user-1',
8
+ entityType: EntityType.ENTITY_TYPE_USER_AVATAR,
9
+ fileIds: ['avatar-file-1'],
10
+ };
11
+ const wireError = (grpcCode, body) => ({
12
+ code: grpcCode,
13
+ details: JSON.stringify({ timestamp: '2026-10-16T10:00:00.000Z', ...body }),
14
+ message: `${String(grpcCode)} ERROR`,
15
+ });
16
+ describe('StorageClientService error mapping (Unit)', () => {
17
+ let service;
18
+ let confirmFileAttachment;
19
+ const failWith = (error) => {
20
+ confirmFileAttachment.mockReturnValue(throwError(() => error));
21
+ };
22
+ beforeEach(() => {
23
+ confirmFileAttachment = jest.fn();
24
+ const clientGrpc = {
25
+ getService: () => ({ confirmFileAttachment }),
26
+ getClientByServiceName: () => ({}),
27
+ };
28
+ service = new StorageClientService(clientGrpc, { timeoutMs: 1000 });
29
+ service.onModuleInit();
30
+ });
31
+ describe('errors raised by ms-storage (JSON body in details)', () => {
32
+ it('relays a not found file with its status and code', async () => {
33
+ failWith(wireError(GrpcStatus.NOT_FOUND, {
34
+ statusCode: 404,
35
+ code: 'FILE_NOT_FOUND',
36
+ message: "File with ID 'avatar-file-1' was not found.",
37
+ details: { fileId: 'avatar-file-1' },
38
+ }));
39
+ const error = await service.confirmFileAttachment(command).catch((e) => e);
40
+ expect(isBaseApiError(error)).toBe(true);
41
+ expect(error).toMatchObject({
42
+ statusCode: 404,
43
+ grpcCode: GrpcStatus.NOT_FOUND,
44
+ code: 'FILE_NOT_FOUND',
45
+ message: "File with ID 'avatar-file-1' was not found.",
46
+ details: { fileId: 'avatar-file-1' },
47
+ });
48
+ });
49
+ it('relays a refused attachment as 422', async () => {
50
+ failWith(wireError(GrpcStatus.FAILED_PRECONDITION, {
51
+ statusCode: 422,
52
+ code: 'FILE_ATTACHMENT_REFUSED',
53
+ message: 'File already reserved for another entity.',
54
+ }));
55
+ await expect(service.confirmFileAttachment(command)).rejects.toMatchObject({
56
+ statusCode: 422,
57
+ grpcCode: GrpcStatus.FAILED_PRECONDITION,
58
+ code: 'FILE_ATTACHMENT_REFUSED',
59
+ });
60
+ });
61
+ });
62
+ describe('failures without a usable body keep their existing error', () => {
63
+ it.each([
64
+ ['a plain gRPC error', { code: 5, details: 'File not found', message: '5 NOT_FOUND' }],
65
+ ['a body that is not JSON', { code: 13, details: '{oops', message: '13 INTERNAL' }],
66
+ [
67
+ 'a JSON body without a status code',
68
+ { code: 13, details: JSON.stringify({ message: 'boom' }), message: '13 INTERNAL' },
69
+ ],
70
+ [
71
+ 'a transport failure (UNAVAILABLE)',
72
+ { code: 14, details: 'No connection established', message: '14 UNAVAILABLE' },
73
+ ],
74
+ ])('still raises STORAGE_ATTACHMENT_FAILED for %s', async (_label, error) => {
75
+ failWith(error);
76
+ await expect(service.confirmFileAttachment(command)).rejects.toMatchObject({
77
+ statusCode: 500,
78
+ code: 'STORAGE_ATTACHMENT_FAILED',
79
+ });
80
+ });
81
+ });
82
+ it('keeps the transport reason readable for the consumers that fall back', async () => {
83
+ failWith({ code: 14, details: 'No connection established', message: '14 UNAVAILABLE' });
84
+ const error = await service.confirmFileAttachment(command).catch((e) => e);
85
+ expect(isStorageUnavailable(error)).toBe(true);
86
+ });
87
+ });
88
+ describe('isStorageUnavailable', () => {
89
+ it.each([
90
+ ['a timeout', STORAGE_TIMEOUT('user-1', 2000)],
91
+ [
92
+ 'an UNAVAILABLE failure',
93
+ STORAGE_ATTACHMENT_FAILED('user-1', '14 UNAVAILABLE: no connection'),
94
+ ],
95
+ ['a DEADLINE_EXCEEDED failure', STORAGE_ATTACHMENT_FAILED('user-1', 'DEADLINE_EXCEEDED')],
96
+ ['a refused connection', STORAGE_ATTACHMENT_FAILED('user-1', 'connect ECONNREFUSED 10.0.0.1')],
97
+ ['a raw UNAVAILABLE gRPC error', { code: 14, details: 'down', message: 'down' }],
98
+ ['a raw DEADLINE_EXCEEDED gRPC error', { code: 4, details: 'slow', message: 'slow' }],
99
+ ])('is true for %s', (_label, error) => {
100
+ expect(isStorageUnavailable(error)).toBe(true);
101
+ });
102
+ it.each([
103
+ ['another attachment failure', STORAGE_ATTACHMENT_FAILED('user-1', 'boom')],
104
+ ['a plain error', new Error('boom')],
105
+ ['a gRPC NOT_FOUND error', { code: 5, details: 'File not found', message: 'File not found' }],
106
+ ['a string', 'UNAVAILABLE'],
107
+ ['nothing', undefined],
108
+ ])('is false for %s', (_label, error) => {
109
+ expect(isStorageUnavailable(error)).toBe(false);
110
+ });
111
+ });
112
+ describe('StorageUnavailableError', () => {
113
+ it('is a 503 UNAVAILABLE API error naming the entity', () => {
114
+ const error = new StorageUnavailableError('user-1');
115
+ expect(isBaseApiError(error)).toBe(true);
116
+ expect(error).toMatchObject({
117
+ statusCode: 503,
118
+ grpcCode: GrpcStatus.UNAVAILABLE,
119
+ code: 'STORAGE_UNAVAILABLE',
120
+ });
121
+ expect(error.message).toContain('user-1');
122
+ });
123
+ });
124
+ //# sourceMappingURL=storage-client-errors.unit.spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"storage-client-errors.unit.spec.js","sourceRoot":"","sources":["../../src/test/storage-client-errors.unit.spec.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@volontariapp/storage-client",
3
- "version": "0.2.2",
3
+ "version": "0.3.0",
4
4
  "publishConfig": {
5
5
  "access": "public",
6
6
  "provenance": true