@ikatec/digisac-api-sdk 4.1.0 → 4.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.
- package/README.md +7 -4
- package/dist/apis/authHistory/AuthHistoryApi.cjs +12 -12
- package/dist/apis/authHistory/AuthHistoryApi.cjs.map +1 -1
- package/dist/apis/authHistory/AuthHistoryApi.d.ts +12 -19
- package/dist/apis/authHistory/AuthHistoryApi.d.ts.map +1 -1
- package/dist/apis/authHistory/AuthHistoryApi.mjs +12 -12
- package/dist/apis/authHistory/AuthHistoryApi.mjs.map +1 -1
- package/dist/apis/authHistory/types.d.ts +4 -3
- package/dist/apis/authHistory/types.d.ts.map +1 -1
- package/dist/apis/customFields/types.d.ts +21 -19
- package/dist/apis/customFields/types.d.ts.map +1 -1
- package/dist/apis/dashboard/DashboardApi.cjs +54 -0
- package/dist/apis/dashboard/DashboardApi.cjs.map +1 -0
- package/dist/apis/dashboard/DashboardApi.d.ts +65 -0
- package/dist/apis/dashboard/DashboardApi.d.ts.map +1 -0
- package/dist/apis/dashboard/DashboardApi.mjs +53 -0
- package/dist/apis/dashboard/DashboardApi.mjs.map +1 -0
- package/dist/apis/dashboard/index.cjs +4 -0
- package/dist/apis/dashboard/index.d.ts +3 -0
- package/dist/apis/dashboard/index.d.ts.map +1 -0
- package/dist/apis/dashboard/index.mjs +3 -0
- package/dist/apis/dashboard/types.cjs +0 -0
- package/dist/apis/dashboard/types.d.ts +124 -0
- package/dist/apis/dashboard/types.d.ts.map +1 -0
- package/dist/apis/dashboard/types.mjs +0 -0
- package/dist/apis/holiday/types.d.ts +34 -8
- package/dist/apis/holiday/types.d.ts.map +1 -1
- package/dist/apis/index.cjs +6 -0
- package/dist/apis/index.d.ts +2 -0
- package/dist/apis/index.d.ts.map +1 -1
- package/dist/apis/index.mjs +5 -1
- package/dist/apis/now/NowApi.cjs +37 -0
- package/dist/apis/now/NowApi.cjs.map +1 -0
- package/dist/apis/now/NowApi.d.ts +26 -0
- package/dist/apis/now/NowApi.d.ts.map +1 -0
- package/dist/apis/now/NowApi.mjs +36 -0
- package/dist/apis/now/NowApi.mjs.map +1 -0
- package/dist/apis/now/index.cjs +4 -0
- package/dist/apis/now/index.d.ts +3 -0
- package/dist/apis/now/index.d.ts.map +1 -0
- package/dist/apis/now/index.mjs +3 -0
- package/dist/apis/now/types.cjs +0 -0
- package/dist/apis/now/types.d.ts +45 -0
- package/dist/apis/now/types.d.ts.map +1 -0
- package/dist/apis/now/types.mjs +0 -0
- package/dist/apis/people/types.d.ts +7 -2
- package/dist/apis/people/types.d.ts.map +1 -1
- package/dist/apis/permissions/PermissionsApi.cjs +6 -2
- package/dist/apis/permissions/PermissionsApi.cjs.map +1 -1
- package/dist/apis/permissions/PermissionsApi.d.ts +7 -3
- package/dist/apis/permissions/PermissionsApi.d.ts.map +1 -1
- package/dist/apis/permissions/PermissionsApi.mjs +6 -2
- package/dist/apis/permissions/PermissionsApi.mjs.map +1 -1
- package/dist/apis/permissions/types.d.ts +4 -14
- package/dist/apis/permissions/types.d.ts.map +1 -1
- package/dist/apis/personalAccessTokens/PersonalAccessTokensApi.cjs +5 -0
- package/dist/apis/personalAccessTokens/PersonalAccessTokensApi.cjs.map +1 -1
- package/dist/apis/personalAccessTokens/PersonalAccessTokensApi.d.ts +5 -0
- package/dist/apis/personalAccessTokens/PersonalAccessTokensApi.d.ts.map +1 -1
- package/dist/apis/personalAccessTokens/PersonalAccessTokensApi.mjs +5 -0
- package/dist/apis/personalAccessTokens/PersonalAccessTokensApi.mjs.map +1 -1
- package/dist/apis/personalAccessTokens/types.d.ts +23 -4
- package/dist/apis/personalAccessTokens/types.d.ts.map +1 -1
- package/dist/apis/roles/RolesApi.cjs.map +1 -1
- package/dist/apis/roles/RolesApi.d.ts +2 -2
- package/dist/apis/roles/RolesApi.d.ts.map +1 -1
- package/dist/apis/roles/RolesApi.mjs.map +1 -1
- package/dist/apis/roles/types.d.ts +27 -4
- package/dist/apis/roles/types.d.ts.map +1 -1
- package/dist/apis/terms/TermsApi.cjs +4 -1
- package/dist/apis/terms/TermsApi.cjs.map +1 -1
- package/dist/apis/terms/TermsApi.d.ts +4 -1
- package/dist/apis/terms/TermsApi.d.ts.map +1 -1
- package/dist/apis/terms/TermsApi.mjs +4 -1
- package/dist/apis/terms/TermsApi.mjs.map +1 -1
- package/dist/apis/timetable/types.d.ts +12 -7
- package/dist/apis/timetable/types.d.ts.map +1 -1
- package/dist/core/BaseCrudApi.cjs +3 -30
- package/dist/core/BaseCrudApi.cjs.map +1 -1
- package/dist/core/BaseCrudApi.d.ts +4 -24
- package/dist/core/BaseCrudApi.d.ts.map +1 -1
- package/dist/core/BaseCrudApi.mjs +3 -30
- package/dist/core/BaseCrudApi.mjs.map +1 -1
- package/dist/core/BaseReadApi.cjs +44 -0
- package/dist/core/BaseReadApi.cjs.map +1 -0
- package/dist/core/BaseReadApi.d.ts +32 -0
- package/dist/core/BaseReadApi.d.ts.map +1 -0
- package/dist/core/BaseReadApi.mjs +43 -0
- package/dist/core/BaseReadApi.mjs.map +1 -0
- package/dist/core/index.cjs +2 -0
- package/dist/core/index.d.ts +1 -0
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.mjs +2 -1
- package/dist/index.cjs +2 -0
- package/dist/index.mjs +2 -1
- package/package.json +1 -1
- package/src/apis/authHistory/AuthHistoryApi.test.ts +10 -2
- package/src/apis/authHistory/AuthHistoryApi.ts +13 -39
- package/src/apis/authHistory/types.ts +4 -3
- package/src/apis/customFields/types.ts +21 -25
- package/src/apis/dashboard/DashboardApi.test-d.ts +49 -0
- package/src/apis/dashboard/DashboardApi.test.ts +270 -0
- package/src/apis/dashboard/DashboardApi.ts +146 -0
- package/src/apis/dashboard/index.ts +2 -0
- package/src/apis/dashboard/types.ts +131 -0
- package/src/apis/holiday/types.ts +36 -8
- package/src/apis/index.ts +2 -0
- package/src/apis/now/NowApi.test.ts +125 -0
- package/src/apis/now/NowApi.ts +59 -0
- package/src/apis/now/index.ts +2 -0
- package/src/apis/now/types.ts +47 -0
- package/src/apis/payloads.test-d.ts +39 -0
- package/src/apis/payloads.test.ts +81 -0
- package/src/apis/people/types.ts +7 -2
- package/src/apis/permissions/PermissionsApi.test.ts +93 -0
- package/src/apis/permissions/PermissionsApi.ts +7 -7
- package/src/apis/permissions/types.ts +4 -16
- package/src/apis/personalAccessTokens/PersonalAccessTokensApi.ts +5 -0
- package/src/apis/personalAccessTokens/types.ts +25 -4
- package/src/apis/queryFormat.test.ts +139 -0
- package/src/apis/roles/RolesApi.ts +7 -2
- package/src/apis/roles/types.ts +27 -4
- package/src/apis/terms/TermsApi.ts +4 -1
- package/src/apis/timetable/types.ts +13 -7
- package/src/core/BaseCrudApi.ts +4 -60
- package/src/core/BaseReadApi.ts +70 -0
- package/src/core/index.ts +1 -0
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { BaseApi } from "./BaseApi.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/core/BaseReadApi.ts
|
|
4
|
+
/**
|
|
5
|
+
* Generic read-only resource: `getMany`, `getOne` and `getById` under `basePath`. Base of
|
|
6
|
+
* `BaseCrudApi`, and of the resources the backend only serves for reading (e.g. permissions,
|
|
7
|
+
* auth history).
|
|
8
|
+
*/
|
|
9
|
+
var BaseReadApi = class extends BaseApi {
|
|
10
|
+
basePath;
|
|
11
|
+
constructor(client, basePath) {
|
|
12
|
+
super(client);
|
|
13
|
+
this.basePath = basePath.replace(/\/$/, "");
|
|
14
|
+
}
|
|
15
|
+
getMany(query, headers) {
|
|
16
|
+
const queryString = this.toQueryString(query);
|
|
17
|
+
return this.client.get(`${this.basePath}${queryString}`, headers);
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Returns the first resource matching the query, or `null` when none match.
|
|
21
|
+
* Convenience over `getMany` with `limit: 1`.
|
|
22
|
+
* @permissions $resourceName.view
|
|
23
|
+
*/
|
|
24
|
+
async getOne(query, headers) {
|
|
25
|
+
return (await this.getMany({
|
|
26
|
+
...query,
|
|
27
|
+
limit: 1,
|
|
28
|
+
paginate: false
|
|
29
|
+
}, headers))?.[0] ?? null;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Returns a single resource by ID. When it does not exist most routes answer 200 with an empty
|
|
33
|
+
* body, which resolves to `undefined`.
|
|
34
|
+
* @permissions $resourceName.view
|
|
35
|
+
*/
|
|
36
|
+
getById(id, query, headers) {
|
|
37
|
+
return this.client.get(`${this.basePath}/${id}${this.toQueryString(query)}`, headers);
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
//#endregion
|
|
42
|
+
export { BaseReadApi };
|
|
43
|
+
//# sourceMappingURL=BaseReadApi.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BaseReadApi.mjs","names":[],"sources":["../../src/core/BaseReadApi.ts"],"sourcesContent":["import type { ApiClient } from './ApiClient'\nimport { BaseApi } from './BaseApi'\nimport type { GetByIdQuery, HasGetById, HasGetMany, ListQuery, Paginated } from './types'\n\n/**\n * Generic read-only resource: `getMany`, `getOne` and `getById` under `basePath`. Base of\n * `BaseCrudApi`, and of the resources the backend only serves for reading (e.g. permissions,\n * auth history).\n */\nexport class BaseReadApi<TResponse, TQuery extends ListQuery<TResponse> = ListQuery<TResponse>>\n extends BaseApi\n implements HasGetMany<TResponse, TQuery>, HasGetById<TResponse>\n{\n public readonly basePath: string\n\n constructor(client: ApiClient, basePath: string) {\n super(client)\n this.basePath = basePath.replace(/\\/$/, '')\n }\n\n getMany(\n query: TQuery & { paginate: false },\n headers?: Record<string, string>,\n ): Promise<TResponse[]>\n getMany(\n query?: TQuery & { paginate?: true },\n headers?: Record<string, string>,\n ): Promise<Paginated<TResponse>>\n getMany(\n query?: TQuery,\n headers?: Record<string, string>,\n ): Promise<Paginated<TResponse> | TResponse[]>\n getMany(\n query?: TQuery,\n headers?: Record<string, string>,\n ): Promise<Paginated<TResponse> | TResponse[]> {\n const queryString = this.toQueryString(query)\n return this.client.get<Paginated<TResponse> | TResponse[]>(\n `${this.basePath}${queryString}`,\n headers,\n )\n }\n\n /**\n * Returns the first resource matching the query, or `null` when none match.\n * Convenience over `getMany` with `limit: 1`.\n * @permissions $resourceName.view\n */\n async getOne(query?: TQuery, headers?: Record<string, string>): Promise<TResponse | null> {\n const results = await this.getMany(\n { ...query, limit: 1, paginate: false } as TQuery & { paginate: false },\n headers,\n )\n // An empty response body resolves to `undefined`.\n return results?.[0] ?? null\n }\n\n /**\n * Returns a single resource by ID. When it does not exist most routes answer 200 with an empty\n * body, which resolves to `undefined`.\n * @permissions $resourceName.view\n */\n getById(\n id: string,\n query?: GetByIdQuery<TResponse>,\n headers?: Record<string, string>,\n ): Promise<TResponse> {\n return this.client.get<TResponse>(`${this.basePath}/${id}${this.toQueryString(query)}`, headers)\n }\n}\n"],"mappings":";;;;;;;;AASA,IAAa,cAAb,cACU,QAEV;CACE,AAAgB;CAEhB,YAAY,QAAmB,UAAkB;AAC/C,QAAM,OAAO;AACb,OAAK,WAAW,SAAS,QAAQ,OAAO,GAAG;;CAe7C,QACE,OACA,SAC6C;EAC7C,MAAM,cAAc,KAAK,cAAc,MAAM;AAC7C,SAAO,KAAK,OAAO,IACjB,GAAG,KAAK,WAAW,eACnB,QACD;;;;;;;CAQH,MAAM,OAAO,OAAgB,SAA6D;AAMxF,UALgB,MAAM,KAAK,QACzB;GAAE,GAAG;GAAO,OAAO;GAAG,UAAU;GAAO,EACvC,QACD,IAEgB,MAAM;;;;;;;CAQzB,QACE,IACA,OACA,SACoB;AACpB,SAAO,KAAK,OAAO,IAAe,GAAG,KAAK,SAAS,GAAG,KAAK,KAAK,cAAc,MAAM,IAAI,QAAQ"}
|
package/dist/core/index.cjs
CHANGED
|
@@ -2,6 +2,7 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
|
2
2
|
const require_core_errors = require('./errors.cjs');
|
|
3
3
|
const require_core_BaseApiClient = require('./BaseApiClient.cjs');
|
|
4
4
|
const require_core_BaseApi = require('./BaseApi.cjs');
|
|
5
|
+
const require_core_BaseReadApi = require('./BaseReadApi.cjs');
|
|
5
6
|
const require_core_BaseCrudApi = require('./BaseCrudApi.cjs');
|
|
6
7
|
const require_core_ArchivableCrudApi = require('./ArchivableCrudApi.cjs');
|
|
7
8
|
|
|
@@ -13,6 +14,7 @@ exports.BadRequestError = require_core_errors.BadRequestError;
|
|
|
13
14
|
exports.BaseApi = require_core_BaseApi.BaseApi;
|
|
14
15
|
exports.BaseApiClient = require_core_BaseApiClient.BaseApiClient;
|
|
15
16
|
exports.BaseCrudApi = require_core_BaseCrudApi.BaseCrudApi;
|
|
17
|
+
exports.BaseReadApi = require_core_BaseReadApi.BaseReadApi;
|
|
16
18
|
exports.ConflictError = require_core_errors.ConflictError;
|
|
17
19
|
exports.DigisacError = require_core_errors.DigisacError;
|
|
18
20
|
exports.InternalServerError = require_core_errors.InternalServerError;
|
package/dist/core/index.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ export type { QueryFormat } from './queryString';
|
|
|
5
5
|
export { DigisacError, ApiConnectionError, ApiError, BadRequestError, ValidationError, AuthenticationError, PaymentRequiredError, PermissionDeniedError, NotFoundError, ConflictError, UnprocessableEntityError, RateLimitError, InternalServerError, parseFieldErrors, } from './errors';
|
|
6
6
|
export type { FieldError, ErrorClassName, ApiErrorOptions } from './errors';
|
|
7
7
|
export { BaseApi } from './BaseApi';
|
|
8
|
+
export { BaseReadApi } from './BaseReadApi';
|
|
8
9
|
export { BaseCrudApi } from './BaseCrudApi';
|
|
9
10
|
export { ArchivableCrudApi } from './ArchivableCrudApi';
|
|
10
11
|
export type { WhereClause, IncludeItem, ListQuery, GetByIdQuery, Paginated } from './types';
|
package/dist/core/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AACxD,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AAC/C,YAAY,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAA;AAC3D,YAAY,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAChD,OAAO,EACL,YAAY,EACZ,kBAAkB,EAClB,QAAQ,EACR,eAAe,EACf,eAAe,EACf,mBAAmB,EACnB,oBAAoB,EACpB,qBAAqB,EACrB,aAAa,EACb,aAAa,EACb,wBAAwB,EACxB,cAAc,EACd,mBAAmB,EACnB,gBAAgB,GACjB,MAAM,UAAU,CAAA;AACjB,YAAY,EAAE,UAAU,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,UAAU,CAAA;AAC3E,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACnC,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAC3C,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAA;AACvD,YAAY,EAAE,WAAW,EAAE,WAAW,EAAE,SAAS,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,SAAS,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AACxD,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AAC/C,YAAY,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAA;AAC3D,YAAY,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAChD,OAAO,EACL,YAAY,EACZ,kBAAkB,EAClB,QAAQ,EACR,eAAe,EACf,eAAe,EACf,mBAAmB,EACnB,oBAAoB,EACpB,qBAAqB,EACrB,aAAa,EACb,aAAa,EACb,wBAAwB,EACxB,cAAc,EACd,mBAAmB,EACnB,gBAAgB,GACjB,MAAM,UAAU,CAAA;AACjB,YAAY,EAAE,UAAU,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,UAAU,CAAA;AAC3E,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACnC,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAC3C,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAC3C,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAA;AACvD,YAAY,EAAE,WAAW,EAAE,WAAW,EAAE,SAAS,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,SAAS,CAAA"}
|
package/dist/core/index.mjs
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { ApiConnectionError, ApiError, AuthenticationError, BadRequestError, ConflictError, DigisacError, InternalServerError, NotFoundError, PaymentRequiredError, PermissionDeniedError, RateLimitError, UnprocessableEntityError, ValidationError, parseFieldErrors } from "./errors.mjs";
|
|
2
2
|
import { BaseApiClient } from "./BaseApiClient.mjs";
|
|
3
3
|
import { BaseApi } from "./BaseApi.mjs";
|
|
4
|
+
import { BaseReadApi } from "./BaseReadApi.mjs";
|
|
4
5
|
import { BaseCrudApi } from "./BaseCrudApi.mjs";
|
|
5
6
|
import { ArchivableCrudApi } from "./ArchivableCrudApi.mjs";
|
|
6
7
|
|
|
7
|
-
export { ApiConnectionError, ApiError, ArchivableCrudApi, AuthenticationError, BadRequestError, BaseApi, BaseApiClient, BaseCrudApi, ConflictError, DigisacError, InternalServerError, NotFoundError, PaymentRequiredError, PermissionDeniedError, RateLimitError, UnprocessableEntityError, ValidationError, parseFieldErrors };
|
|
8
|
+
export { ApiConnectionError, ApiError, ArchivableCrudApi, AuthenticationError, BadRequestError, BaseApi, BaseApiClient, BaseCrudApi, BaseReadApi, ConflictError, DigisacError, InternalServerError, NotFoundError, PaymentRequiredError, PermissionDeniedError, RateLimitError, UnprocessableEntityError, ValidationError, parseFieldErrors };
|
package/dist/index.cjs
CHANGED
|
@@ -2,6 +2,7 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
|
2
2
|
const require_core_errors = require('./core/errors.cjs');
|
|
3
3
|
const require_core_BaseApiClient = require('./core/BaseApiClient.cjs');
|
|
4
4
|
const require_core_BaseApi = require('./core/BaseApi.cjs');
|
|
5
|
+
const require_core_BaseReadApi = require('./core/BaseReadApi.cjs');
|
|
5
6
|
const require_core_BaseCrudApi = require('./core/BaseCrudApi.cjs');
|
|
6
7
|
const require_core_ArchivableCrudApi = require('./core/ArchivableCrudApi.cjs');
|
|
7
8
|
require('./core/index.cjs');
|
|
@@ -14,6 +15,7 @@ exports.BadRequestError = require_core_errors.BadRequestError;
|
|
|
14
15
|
exports.BaseApi = require_core_BaseApi.BaseApi;
|
|
15
16
|
exports.BaseApiClient = require_core_BaseApiClient.BaseApiClient;
|
|
16
17
|
exports.BaseCrudApi = require_core_BaseCrudApi.BaseCrudApi;
|
|
18
|
+
exports.BaseReadApi = require_core_BaseReadApi.BaseReadApi;
|
|
17
19
|
exports.ConflictError = require_core_errors.ConflictError;
|
|
18
20
|
exports.DigisacError = require_core_errors.DigisacError;
|
|
19
21
|
exports.InternalServerError = require_core_errors.InternalServerError;
|
package/dist/index.mjs
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { ApiConnectionError, ApiError, AuthenticationError, BadRequestError, ConflictError, DigisacError, InternalServerError, NotFoundError, PaymentRequiredError, PermissionDeniedError, RateLimitError, UnprocessableEntityError, ValidationError, parseFieldErrors } from "./core/errors.mjs";
|
|
2
2
|
import { BaseApiClient } from "./core/BaseApiClient.mjs";
|
|
3
3
|
import { BaseApi } from "./core/BaseApi.mjs";
|
|
4
|
+
import { BaseReadApi } from "./core/BaseReadApi.mjs";
|
|
4
5
|
import { BaseCrudApi } from "./core/BaseCrudApi.mjs";
|
|
5
6
|
import { ArchivableCrudApi } from "./core/ArchivableCrudApi.mjs";
|
|
6
7
|
import "./core/index.mjs";
|
|
7
8
|
|
|
8
|
-
export { ApiConnectionError, ApiError, ArchivableCrudApi, AuthenticationError, BadRequestError, BaseApi, BaseApiClient, BaseCrudApi, ConflictError, DigisacError, InternalServerError, NotFoundError, PaymentRequiredError, PermissionDeniedError, RateLimitError, UnprocessableEntityError, ValidationError, parseFieldErrors };
|
|
9
|
+
export { ApiConnectionError, ApiError, ArchivableCrudApi, AuthenticationError, BadRequestError, BaseApi, BaseApiClient, BaseCrudApi, BaseReadApi, ConflictError, DigisacError, InternalServerError, NotFoundError, PaymentRequiredError, PermissionDeniedError, RateLimitError, UnprocessableEntityError, ValidationError, parseFieldErrors };
|
package/package.json
CHANGED
|
@@ -25,7 +25,6 @@ const entry: AuthHistory = {
|
|
|
25
25
|
event: 'auth',
|
|
26
26
|
userId: 'user-1',
|
|
27
27
|
accountId: 'acc-1',
|
|
28
|
-
branch: 'main',
|
|
29
28
|
originIP: '127.0.0.1',
|
|
30
29
|
originUA: 'Mozilla/5.0',
|
|
31
30
|
createdAt: '2024-01-01T00:00:00Z',
|
|
@@ -95,10 +94,19 @@ describe('AuthHistoryApi', () => {
|
|
|
95
94
|
expect(result).toEqual(entry)
|
|
96
95
|
})
|
|
97
96
|
|
|
97
|
+
it('sends the query (e.g. include) in the query string, not as headers', async () => {
|
|
98
|
+
vi.mocked(client.get).mockResolvedValue(entry)
|
|
99
|
+
await api.getById('ah-1', { include: ['user'] })
|
|
100
|
+
expect(client.get).toHaveBeenCalledWith(
|
|
101
|
+
`/auth-history/ah-1?query=${encodeURIComponent(JSON.stringify({ include: ['user'] }))}`,
|
|
102
|
+
undefined,
|
|
103
|
+
)
|
|
104
|
+
})
|
|
105
|
+
|
|
98
106
|
it('forwards custom headers', async () => {
|
|
99
107
|
vi.mocked(client.get).mockResolvedValue(entry)
|
|
100
108
|
const headers = { 'X-Custom': 'h' }
|
|
101
|
-
await api.getById('ah-1', headers)
|
|
109
|
+
await api.getById('ah-1', undefined, headers)
|
|
102
110
|
expect(client.get).toHaveBeenCalledWith('/auth-history/ah-1', headers)
|
|
103
111
|
})
|
|
104
112
|
|
|
@@ -1,43 +1,17 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
1
|
+
import type { ApiClient } from '../../core/ApiClient'
|
|
2
|
+
import { BaseReadApi } from '../../core/BaseReadApi'
|
|
3
3
|
import type { AuthHistory } from './types'
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
): Promise<AuthHistory[]>
|
|
17
|
-
getMany(
|
|
18
|
-
query?: ListQuery<AuthHistory> & { paginate?: true },
|
|
19
|
-
headers?: Record<string, string>,
|
|
20
|
-
): Promise<Paginated<AuthHistory>>
|
|
21
|
-
getMany(
|
|
22
|
-
query?: ListQuery<AuthHistory>,
|
|
23
|
-
headers?: Record<string, string>,
|
|
24
|
-
): Promise<Paginated<AuthHistory> | AuthHistory[]>
|
|
25
|
-
getMany(
|
|
26
|
-
query?: ListQuery<AuthHistory>,
|
|
27
|
-
headers?: Record<string, string>,
|
|
28
|
-
): Promise<Paginated<AuthHistory> | AuthHistory[]> {
|
|
29
|
-
const queryString = this.toQueryString(query)
|
|
30
|
-
return this.client.get<Paginated<AuthHistory> | AuthHistory[]>(
|
|
31
|
-
`/auth-history${queryString}`,
|
|
32
|
-
headers,
|
|
33
|
-
)
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* Returns a single authentication event by its ID. Non-admin users can only access their own records.
|
|
38
|
-
* @permissions authHistory.view
|
|
39
|
-
*/
|
|
40
|
-
getById(id: string, headers?: Record<string, string>): Promise<AuthHistory> {
|
|
41
|
-
return this.client.get<AuthHistory>(`/auth-history/${id}`, headers)
|
|
5
|
+
/**
|
|
6
|
+
* Authentication events (auth, password_change, logout), read-only (guarded by
|
|
7
|
+
* `authHistory.view`). Non-admin users can only see and access their own records.
|
|
8
|
+
*
|
|
9
|
+
* Since 4.2 `getById` has the base signature `(id, query?, headers?)`: a call written as
|
|
10
|
+
* `getById(id, headers)` with the headers in a variable still compiles, but the headers are sent
|
|
11
|
+
* as the query string — move them to the third argument.
|
|
12
|
+
*/
|
|
13
|
+
export class AuthHistoryApi extends BaseReadApi<AuthHistory> {
|
|
14
|
+
constructor(client: ApiClient) {
|
|
15
|
+
super(client, '/auth-history')
|
|
42
16
|
}
|
|
43
17
|
}
|
|
@@ -8,9 +8,10 @@ export type AuthHistory = {
|
|
|
8
8
|
event: AuthHistoryEvent
|
|
9
9
|
userId: string
|
|
10
10
|
accountId: string
|
|
11
|
-
|
|
12
|
-
originIP: string
|
|
13
|
-
|
|
11
|
+
/** Nullable in the database. */
|
|
12
|
+
originIP: string | null
|
|
13
|
+
/** Nullable in the database. */
|
|
14
|
+
originUA: string | null
|
|
14
15
|
createdAt: string
|
|
15
16
|
updatedAt: string
|
|
16
17
|
// relationships
|
|
@@ -17,33 +17,29 @@ export type CustomFieldType =
|
|
|
17
17
|
| 'list'
|
|
18
18
|
|
|
19
19
|
export type CustomFieldOption = {
|
|
20
|
-
id?: string
|
|
21
|
-
label?: string
|
|
20
|
+
id?: string | null
|
|
21
|
+
label?: string | null
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
/**
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
/**
|
|
25
|
+
* Settings of a custom field — a free JSONB the backend does not validate, echoed back as sent.
|
|
26
|
+
* The fields below are the ones the web app form sends (`front/src/pages/custom-fields/components/
|
|
27
|
+
* form.tsx`): `options` for the `list` and `checkbox` types, `min`/`max` for `numeric` and `monetary`,
|
|
28
|
+
* `currency` for `monetary`, `maxLength` for `text` and `long-text`, and `placeholder` for the inputs.
|
|
29
|
+
*/
|
|
30
|
+
export type CustomFieldSettings = {
|
|
31
|
+
options?: CustomFieldOption[]
|
|
32
|
+
placeholder?: string | null
|
|
33
|
+
currency?: string | null
|
|
34
|
+
maxLength?: number | null
|
|
35
|
+
min?: string | null
|
|
36
|
+
max?: string | null
|
|
27
37
|
}
|
|
28
38
|
|
|
29
|
-
/**
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
max?: string
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
/** Settings for `text` and `long-text` types: optional max length constraint. */
|
|
37
|
-
export type CustomFieldTextSettings = {
|
|
38
|
-
options: CustomFieldOption[]
|
|
39
|
-
maxLength?: number
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
export type CustomFieldSettings =
|
|
43
|
-
| CustomFieldListSettings
|
|
44
|
-
| CustomFieldMonetarySettings
|
|
45
|
-
| CustomFieldTextSettings
|
|
46
|
-
|
|
39
|
+
/**
|
|
40
|
+
* A custom field of the account. Custom fields are soft-deleted, but `deletedAt` is never returned
|
|
41
|
+
* (customFieldsTransformer).
|
|
42
|
+
*/
|
|
47
43
|
export type CustomField = {
|
|
48
44
|
id: string
|
|
49
45
|
name: string
|
|
@@ -55,7 +51,6 @@ export type CustomField = {
|
|
|
55
51
|
accountId: string
|
|
56
52
|
createdAt: string
|
|
57
53
|
updatedAt: string
|
|
58
|
-
deletedAt: string | null
|
|
59
54
|
}
|
|
60
55
|
|
|
61
56
|
export type CreateCustomFieldPayload = {
|
|
@@ -64,12 +59,13 @@ export type CreateCustomFieldPayload = {
|
|
|
64
59
|
allowed: 'contacts'
|
|
65
60
|
showOnRegister?: boolean
|
|
66
61
|
required?: boolean
|
|
67
|
-
settings?: CustomFieldSettings
|
|
62
|
+
settings?: CustomFieldSettings | null
|
|
68
63
|
}
|
|
69
64
|
|
|
70
65
|
export type UpdateCustomFieldPayload = {
|
|
71
66
|
name?: string
|
|
72
67
|
type?: CustomFieldType
|
|
68
|
+
allowed?: 'contacts'
|
|
73
69
|
showOnRegister?: boolean
|
|
74
70
|
required?: boolean
|
|
75
71
|
settings?: CustomFieldSettings | null
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { expectError, expectType } from 'tsd'
|
|
2
|
+
import type { ApiClient } from '../../core/ApiClient'
|
|
3
|
+
import { DashboardApi } from './DashboardApi'
|
|
4
|
+
import type {
|
|
5
|
+
DashboardDepartmentItem,
|
|
6
|
+
DashboardGrouped,
|
|
7
|
+
DashboardPeriodItem,
|
|
8
|
+
DashboardQuery,
|
|
9
|
+
DashboardUserItem,
|
|
10
|
+
} from './types'
|
|
11
|
+
|
|
12
|
+
declare const client: ApiClient
|
|
13
|
+
declare const filters: DashboardQuery
|
|
14
|
+
declare const flag: boolean
|
|
15
|
+
const dashboard = new DashboardApi(client)
|
|
16
|
+
|
|
17
|
+
// ── without `withTotals` (or `false`): the plain array ──────────────
|
|
18
|
+
|
|
19
|
+
expectType<Promise<DashboardPeriodItem[]>>(dashboard.getByPeriod())
|
|
20
|
+
expectType<Promise<DashboardPeriodItem[]>>(dashboard.getByPeriod({ grouping: 'weekly' }))
|
|
21
|
+
expectType<Promise<DashboardPeriodItem[]>>(dashboard.getByPeriod({ withTotals: false }))
|
|
22
|
+
expectType<Promise<DashboardPeriodItem[]>>(dashboard.getByPeriod(filters))
|
|
23
|
+
expectType<Promise<DashboardUserItem[]>>(dashboard.getByUser(filters))
|
|
24
|
+
expectType<Promise<DashboardDepartmentItem[]>>(dashboard.getByDepartment(filters))
|
|
25
|
+
|
|
26
|
+
// ── `withTotals: true`: `{ totals, items }` ─────────────────────────
|
|
27
|
+
|
|
28
|
+
expectType<Promise<DashboardGrouped<DashboardPeriodItem>>>(
|
|
29
|
+
dashboard.getByPeriod({ withTotals: true }),
|
|
30
|
+
)
|
|
31
|
+
expectType<Promise<DashboardGrouped<DashboardPeriodItem>>>(
|
|
32
|
+
dashboard.getByPeriod({ ...filters, withTotals: true }),
|
|
33
|
+
)
|
|
34
|
+
expectType<Promise<DashboardGrouped<DashboardUserItem>>>(dashboard.getByUser({ withTotals: true }))
|
|
35
|
+
expectType<Promise<DashboardGrouped<DashboardDepartmentItem>>>(
|
|
36
|
+
dashboard.getByDepartment({ withTotals: true }),
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
// ── `withTotals` only known at runtime: the union ───────────────────
|
|
40
|
+
|
|
41
|
+
expectType<Promise<DashboardPeriodItem[] | DashboardGrouped<DashboardPeriodItem>>>(
|
|
42
|
+
dashboard.getByPeriod({ ...filters, withTotals: flag }),
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
// ── what the backend does not take ──────────────────────────────────
|
|
46
|
+
|
|
47
|
+
expectError(dashboard.getByPeriod({ grouping: 'quarterly' }))
|
|
48
|
+
expectError(dashboard.getByTopic({ withTotals: true }))
|
|
49
|
+
expectError(dashboard.getByService({ withTotals: true }))
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
|
2
|
+
import { DashboardApi } from './DashboardApi'
|
|
3
|
+
import type { ApiClient } from '../../core/ApiClient'
|
|
4
|
+
import type {
|
|
5
|
+
DashboardDepartmentItem,
|
|
6
|
+
DashboardGeneral,
|
|
7
|
+
DashboardPeriodItem,
|
|
8
|
+
DashboardQuery,
|
|
9
|
+
DashboardServiceItem,
|
|
10
|
+
DashboardTopicItem,
|
|
11
|
+
DashboardUserItem,
|
|
12
|
+
DashboardWithTotals,
|
|
13
|
+
} from './types'
|
|
14
|
+
|
|
15
|
+
// ── Mock client factory ──────────────────────────────────────────────
|
|
16
|
+
|
|
17
|
+
function createMockClient(): ApiClient {
|
|
18
|
+
return {
|
|
19
|
+
setAccessToken: vi.fn().mockReturnThis(),
|
|
20
|
+
request: vi.fn(),
|
|
21
|
+
get: vi.fn(),
|
|
22
|
+
post: vi.fn(),
|
|
23
|
+
put: vi.fn(),
|
|
24
|
+
patch: vi.fn(),
|
|
25
|
+
delete: vi.fn(),
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// ── Fixtures ─────────────────────────────────────────────────────────
|
|
30
|
+
|
|
31
|
+
const general: DashboardGeneral = {
|
|
32
|
+
sentMessagesCount: 10,
|
|
33
|
+
receivedMessagesCount: 5,
|
|
34
|
+
totalMessagesCount: 15,
|
|
35
|
+
openedTicketsCount: 2,
|
|
36
|
+
closedTicketsCount: 3,
|
|
37
|
+
totalTicketsCount: 5,
|
|
38
|
+
waitingTime: 60,
|
|
39
|
+
waitingTimeAfterBot: 30,
|
|
40
|
+
waitingTimeAvg: 45,
|
|
41
|
+
ticketTime: 600,
|
|
42
|
+
contactsCount: 4,
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const periodItems: DashboardPeriodItem[] = [
|
|
46
|
+
{ name: '01/09/2026', sentMessagesCount: 4, receivedMessagesCount: 2, totalMessagesCount: 6 },
|
|
47
|
+
{ name: '02/09/2026', openedTicketsCount: 1, closedTicketsCount: 1, totalTicketsCount: 2 },
|
|
48
|
+
]
|
|
49
|
+
|
|
50
|
+
const userItems: DashboardUserItem[] = [
|
|
51
|
+
{ name: 'Alice', sentMessagesCount: 7, closedTicketsCount: 3, ticketTopicsCount: 1 },
|
|
52
|
+
]
|
|
53
|
+
|
|
54
|
+
const departmentItems: DashboardDepartmentItem[] = [
|
|
55
|
+
{ name: 'Suporte', openedTicketsCount: 2, waitingTime: 60, ticketTime: 600 },
|
|
56
|
+
]
|
|
57
|
+
|
|
58
|
+
const topicItems: DashboardTopicItem[] = [
|
|
59
|
+
{ name: 'Financeiro', openedTicketsCount: 1, waitingTimeAfterBot: 30, waitingTimeAvg: 45 },
|
|
60
|
+
]
|
|
61
|
+
|
|
62
|
+
const serviceItems: DashboardServiceItem[] = [
|
|
63
|
+
{ name: 'WhatsApp', receivedMessagesCount: 5, contactsCount: 4, ticketTopicsCount: 2 },
|
|
64
|
+
]
|
|
65
|
+
|
|
66
|
+
const filters: DashboardQuery = {
|
|
67
|
+
startPeriod: '2026-09-01T00:00:00.000Z',
|
|
68
|
+
endPeriod: '2026-09-07T23:59:59.999Z',
|
|
69
|
+
status: 'closed',
|
|
70
|
+
departmentId: ['d1'],
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const queryString = (query: DashboardQuery & DashboardWithTotals) =>
|
|
74
|
+
`?query=${encodeURIComponent(JSON.stringify(query))}`
|
|
75
|
+
|
|
76
|
+
// ── Tests ────────────────────────────────────────────────────────────
|
|
77
|
+
|
|
78
|
+
describe('DashboardApi', () => {
|
|
79
|
+
let client: ReturnType<typeof createMockClient>
|
|
80
|
+
let api: DashboardApi
|
|
81
|
+
|
|
82
|
+
beforeEach(() => {
|
|
83
|
+
client = createMockClient()
|
|
84
|
+
api = new DashboardApi(client)
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
it('has no CRUD routes', () => {
|
|
88
|
+
expect(api.basePath).toBe('/dashboard')
|
|
89
|
+
expect(api).not.toHaveProperty('getMany')
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
describe('getGeneral', () => {
|
|
93
|
+
it('calls client.get with /dashboard/general and no query string', async () => {
|
|
94
|
+
vi.mocked(client.get).mockResolvedValue(general)
|
|
95
|
+
const result = await api.getGeneral()
|
|
96
|
+
expect(client.get).toHaveBeenCalledWith('/dashboard/general', undefined)
|
|
97
|
+
expect(result).toEqual(general)
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
it('sends the filters in the query string', async () => {
|
|
101
|
+
vi.mocked(client.get).mockResolvedValue(general)
|
|
102
|
+
await api.getGeneral(filters)
|
|
103
|
+
expect(client.get).toHaveBeenCalledWith(
|
|
104
|
+
`/dashboard/general${queryString(filters)}`,
|
|
105
|
+
undefined,
|
|
106
|
+
)
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
it('forwards custom headers', async () => {
|
|
110
|
+
vi.mocked(client.get).mockResolvedValue(general)
|
|
111
|
+
const headers = { 'X-Custom': 'h' }
|
|
112
|
+
await api.getGeneral(undefined, headers)
|
|
113
|
+
expect(client.get).toHaveBeenCalledWith('/dashboard/general', headers)
|
|
114
|
+
})
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
describe('getByPeriod', () => {
|
|
118
|
+
it('calls client.get with /dashboard/by-period and no query string', async () => {
|
|
119
|
+
vi.mocked(client.get).mockResolvedValue(periodItems)
|
|
120
|
+
const result = await api.getByPeriod()
|
|
121
|
+
expect(client.get).toHaveBeenCalledWith('/dashboard/by-period', undefined)
|
|
122
|
+
expect(result).toEqual(periodItems)
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
it('sends the filters and the grouping in the query string', async () => {
|
|
126
|
+
vi.mocked(client.get).mockResolvedValue(periodItems)
|
|
127
|
+
const query: DashboardQuery = { ...filters, grouping: 'weekly' }
|
|
128
|
+
await api.getByPeriod(query)
|
|
129
|
+
expect(client.get).toHaveBeenCalledWith(
|
|
130
|
+
`/dashboard/by-period${queryString(query)}`,
|
|
131
|
+
undefined,
|
|
132
|
+
)
|
|
133
|
+
})
|
|
134
|
+
|
|
135
|
+
it('answers { totals, items } with withTotals: true', async () => {
|
|
136
|
+
const grouped = { totals: general, items: periodItems }
|
|
137
|
+
vi.mocked(client.get).mockResolvedValue(grouped)
|
|
138
|
+
const query = { ...filters, withTotals: true as const }
|
|
139
|
+
const result = await api.getByPeriod(query)
|
|
140
|
+
expect(client.get).toHaveBeenCalledWith(
|
|
141
|
+
`/dashboard/by-period${queryString(query)}`,
|
|
142
|
+
undefined,
|
|
143
|
+
)
|
|
144
|
+
expect(result).toEqual(grouped)
|
|
145
|
+
expect(result.totals.totalTicketsCount).toBe(5)
|
|
146
|
+
expect(result.items).toHaveLength(2)
|
|
147
|
+
})
|
|
148
|
+
|
|
149
|
+
it('does not send withTotals: false (the backend reads it as truthy in the qs format)', async () => {
|
|
150
|
+
vi.mocked(client.get).mockResolvedValue(periodItems)
|
|
151
|
+
await api.getByPeriod({ ...filters, withTotals: false })
|
|
152
|
+
expect(client.get).toHaveBeenCalledWith(
|
|
153
|
+
`/dashboard/by-period${queryString(filters)}`,
|
|
154
|
+
undefined,
|
|
155
|
+
)
|
|
156
|
+
await api.withQueryFormat('qs').getByPeriod({ withTotals: false, grouping: 'weekly' })
|
|
157
|
+
expect(client.get).toHaveBeenLastCalledWith('/dashboard/by-period?grouping=weekly', undefined)
|
|
158
|
+
await api.getByPeriod({ withTotals: false })
|
|
159
|
+
expect(client.get).toHaveBeenLastCalledWith('/dashboard/by-period', undefined)
|
|
160
|
+
})
|
|
161
|
+
|
|
162
|
+
it('forwards custom headers', async () => {
|
|
163
|
+
vi.mocked(client.get).mockResolvedValue(periodItems)
|
|
164
|
+
const headers = { 'X-Custom': 'h' }
|
|
165
|
+
await api.getByPeriod(undefined, headers)
|
|
166
|
+
expect(client.get).toHaveBeenCalledWith('/dashboard/by-period', headers)
|
|
167
|
+
})
|
|
168
|
+
})
|
|
169
|
+
|
|
170
|
+
describe('getByUser', () => {
|
|
171
|
+
it('calls client.get with /dashboard/by-user and the filters', async () => {
|
|
172
|
+
vi.mocked(client.get).mockResolvedValue(userItems)
|
|
173
|
+
const result = await api.getByUser(filters)
|
|
174
|
+
expect(client.get).toHaveBeenCalledWith(
|
|
175
|
+
`/dashboard/by-user${queryString(filters)}`,
|
|
176
|
+
undefined,
|
|
177
|
+
)
|
|
178
|
+
expect(result).toEqual(userItems)
|
|
179
|
+
})
|
|
180
|
+
|
|
181
|
+
it('answers { totals, items } with withTotals: true', async () => {
|
|
182
|
+
const grouped = { totals: general, items: userItems }
|
|
183
|
+
vi.mocked(client.get).mockResolvedValue(grouped)
|
|
184
|
+
const result = await api.getByUser({ withTotals: true })
|
|
185
|
+
expect(client.get).toHaveBeenCalledWith(
|
|
186
|
+
`/dashboard/by-user${queryString({ withTotals: true })}`,
|
|
187
|
+
undefined,
|
|
188
|
+
)
|
|
189
|
+
expect(result.items[0]?.name).toBe('Alice')
|
|
190
|
+
})
|
|
191
|
+
|
|
192
|
+
it('forwards custom headers', async () => {
|
|
193
|
+
vi.mocked(client.get).mockResolvedValue(userItems)
|
|
194
|
+
const headers = { 'X-Custom': 'h' }
|
|
195
|
+
await api.getByUser(undefined, headers)
|
|
196
|
+
expect(client.get).toHaveBeenCalledWith('/dashboard/by-user', headers)
|
|
197
|
+
})
|
|
198
|
+
})
|
|
199
|
+
|
|
200
|
+
describe('getByDepartment', () => {
|
|
201
|
+
it('calls client.get with /dashboard/by-department and the filters', async () => {
|
|
202
|
+
vi.mocked(client.get).mockResolvedValue(departmentItems)
|
|
203
|
+
const result = await api.getByDepartment(filters)
|
|
204
|
+
expect(client.get).toHaveBeenCalledWith(
|
|
205
|
+
`/dashboard/by-department${queryString(filters)}`,
|
|
206
|
+
undefined,
|
|
207
|
+
)
|
|
208
|
+
expect(result).toEqual(departmentItems)
|
|
209
|
+
})
|
|
210
|
+
|
|
211
|
+
it('answers { totals, items } with withTotals: true', async () => {
|
|
212
|
+
const grouped = { totals: general, items: departmentItems }
|
|
213
|
+
vi.mocked(client.get).mockResolvedValue(grouped)
|
|
214
|
+
const result = await api.getByDepartment({ withTotals: true })
|
|
215
|
+
expect(result).toEqual(grouped)
|
|
216
|
+
})
|
|
217
|
+
|
|
218
|
+
it('forwards custom headers', async () => {
|
|
219
|
+
vi.mocked(client.get).mockResolvedValue(departmentItems)
|
|
220
|
+
const headers = { 'X-Custom': 'h' }
|
|
221
|
+
await api.getByDepartment(undefined, headers)
|
|
222
|
+
expect(client.get).toHaveBeenCalledWith('/dashboard/by-department', headers)
|
|
223
|
+
})
|
|
224
|
+
})
|
|
225
|
+
|
|
226
|
+
describe('getByTopic', () => {
|
|
227
|
+
it('calls client.get with /dashboard/by-topic and the filters', async () => {
|
|
228
|
+
vi.mocked(client.get).mockResolvedValue(topicItems)
|
|
229
|
+
const result = await api.getByTopic(filters)
|
|
230
|
+
expect(client.get).toHaveBeenCalledWith(
|
|
231
|
+
`/dashboard/by-topic${queryString(filters)}`,
|
|
232
|
+
undefined,
|
|
233
|
+
)
|
|
234
|
+
expect(result).toEqual(topicItems)
|
|
235
|
+
})
|
|
236
|
+
|
|
237
|
+
it('forwards custom headers', async () => {
|
|
238
|
+
vi.mocked(client.get).mockResolvedValue(topicItems)
|
|
239
|
+
const headers = { 'X-Custom': 'h' }
|
|
240
|
+
await api.getByTopic(undefined, headers)
|
|
241
|
+
expect(client.get).toHaveBeenCalledWith('/dashboard/by-topic', headers)
|
|
242
|
+
})
|
|
243
|
+
})
|
|
244
|
+
|
|
245
|
+
describe('getByService', () => {
|
|
246
|
+
it('calls client.get with /dashboard/by-service and the filters', async () => {
|
|
247
|
+
vi.mocked(client.get).mockResolvedValue(serviceItems)
|
|
248
|
+
const result = await api.getByService(filters)
|
|
249
|
+
expect(client.get).toHaveBeenCalledWith(
|
|
250
|
+
`/dashboard/by-service${queryString(filters)}`,
|
|
251
|
+
undefined,
|
|
252
|
+
)
|
|
253
|
+
expect(result).toEqual(serviceItems)
|
|
254
|
+
})
|
|
255
|
+
|
|
256
|
+
it('forwards custom headers', async () => {
|
|
257
|
+
vi.mocked(client.get).mockResolvedValue(serviceItems)
|
|
258
|
+
const headers = { 'X-Custom': 'h' }
|
|
259
|
+
await api.getByService(undefined, headers)
|
|
260
|
+
expect(client.get).toHaveBeenCalledWith('/dashboard/by-service', headers)
|
|
261
|
+
})
|
|
262
|
+
})
|
|
263
|
+
|
|
264
|
+
describe('error propagation', () => {
|
|
265
|
+
it('propagates errors from the grouped routes', async () => {
|
|
266
|
+
vi.mocked(client.get).mockRejectedValue(new Error('Unauthorized'))
|
|
267
|
+
await expect(api.getByPeriod()).rejects.toThrow('Unauthorized')
|
|
268
|
+
})
|
|
269
|
+
})
|
|
270
|
+
})
|