rotacloud 1.0.53 → 1.0.56
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/.eslintrc.json +2 -2
- package/.github/workflows/check.yml +36 -0
- package/.github/workflows/publish.yml +10 -8
- package/dist/cjs/interfaces/index.js +5 -1
- package/dist/cjs/interfaces/query-params/index.js +5 -1
- package/dist/cjs/models/index.js +5 -1
- package/dist/cjs/rotacloud.js +5 -1
- package/dist/cjs/services/accounts.service.js +12 -8
- package/dist/cjs/services/attendance.service.d.ts +1 -1
- package/dist/cjs/services/attendance.service.js +13 -11
- package/dist/cjs/services/availability.service.js +11 -11
- package/dist/cjs/services/daily-budgets.service.js +12 -8
- package/dist/cjs/services/daily-revenue.service.js +12 -8
- package/dist/cjs/services/day-notes.service.js +12 -8
- package/dist/cjs/services/days-off.service.js +12 -8
- package/dist/cjs/services/groups.service.d.ts +1 -1
- package/dist/cjs/services/groups.service.js +12 -8
- package/dist/cjs/services/index.js +5 -1
- package/dist/cjs/services/leave-embargoes.service.d.ts +1 -1
- package/dist/cjs/services/leave-embargoes.service.js +12 -8
- package/dist/cjs/services/leave-request.service.d.ts +1 -1
- package/dist/cjs/services/leave-request.service.js +12 -8
- package/dist/cjs/services/leave.service.d.ts +1 -1
- package/dist/cjs/services/leave.service.js +18 -12
- package/dist/cjs/services/locations.service.d.ts +1 -1
- package/dist/cjs/services/locations.service.js +12 -8
- package/dist/cjs/services/roles.service.d.ts +1 -1
- package/dist/cjs/services/roles.service.js +12 -8
- package/dist/cjs/services/service.d.ts +9 -11
- package/dist/cjs/services/service.js +33 -55
- package/dist/cjs/services/shifts.service.d.ts +27 -5
- package/dist/cjs/services/shifts.service.js +40 -14
- package/dist/cjs/services/terminals-active.service.js +12 -8
- package/dist/cjs/services/terminals.service.d.ts +1 -1
- package/dist/cjs/services/terminals.service.js +12 -8
- package/dist/cjs/services/toil-accruals.service.d.ts +1 -1
- package/dist/cjs/services/toil-accruals.service.js +12 -8
- package/dist/cjs/services/toil-allowance.service.js +12 -8
- package/dist/cjs/services/users-clock-in.service.d.ts +2 -2
- package/dist/cjs/services/users-clock-in.service.js +12 -8
- package/dist/cjs/services/users.service.d.ts +1 -1
- package/dist/cjs/services/users.service.js +12 -8
- package/dist/cjs/version.js +1 -1
- package/dist/mjs/services/attendance.service.d.ts +1 -1
- package/dist/mjs/services/attendance.service.js +1 -3
- package/dist/mjs/services/availability.service.js +5 -7
- package/dist/mjs/services/groups.service.d.ts +1 -1
- package/dist/mjs/services/leave-embargoes.service.d.ts +1 -1
- package/dist/mjs/services/leave-request.service.d.ts +1 -1
- package/dist/mjs/services/leave.service.d.ts +1 -1
- package/dist/mjs/services/locations.service.d.ts +1 -1
- package/dist/mjs/services/roles.service.d.ts +1 -1
- package/dist/mjs/services/service.d.ts +9 -11
- package/dist/mjs/services/service.js +34 -57
- package/dist/mjs/services/shifts.service.d.ts +27 -5
- package/dist/mjs/services/shifts.service.js +28 -6
- package/dist/mjs/services/terminals.service.d.ts +1 -1
- package/dist/mjs/services/toil-accruals.service.d.ts +1 -1
- package/dist/mjs/services/users-clock-in.service.d.ts +2 -2
- package/dist/mjs/services/users.service.d.ts +1 -1
- package/dist/mjs/version.js +1 -1
- package/package.json +16 -16
- package/src/services/attendance.service.ts +1 -3
- package/src/services/availability.service.ts +2 -4
- package/src/services/service.ts +51 -82
- package/src/services/shifts.service.ts +45 -14
- package/src/version.ts +1 -1
|
@@ -48,10 +48,18 @@ export class Service {
|
|
|
48
48
|
isLeaveRequest(endpoint) {
|
|
49
49
|
return endpoint === '/leave_requests';
|
|
50
50
|
}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
51
|
+
buildQueryParams(options, extraParams) {
|
|
52
|
+
const queryParams = {
|
|
53
|
+
expand: options?.expand,
|
|
54
|
+
fields: options?.fields,
|
|
55
|
+
limit: options?.limit,
|
|
56
|
+
offset: options?.offset,
|
|
57
|
+
dry_run: options?.dryRun,
|
|
58
|
+
...extraParams,
|
|
59
|
+
// NOTE: Should not overridable so must come after spread of params
|
|
60
|
+
exclude_link_header: true,
|
|
61
|
+
};
|
|
62
|
+
const reducedParams = Object.entries(queryParams ?? {}).reduce((params, [key, val]) => {
|
|
55
63
|
if (val !== undefined && val !== '') {
|
|
56
64
|
if (Array.isArray(val))
|
|
57
65
|
params.push(...val.map((item) => [`${key}[]`, String(item)]));
|
|
@@ -60,17 +68,9 @@ export class Service {
|
|
|
60
68
|
}
|
|
61
69
|
return params;
|
|
62
70
|
}, []);
|
|
63
|
-
return new URLSearchParams(reducedParams)
|
|
64
|
-
}
|
|
65
|
-
parsePageLinkHeader(linkHeader) {
|
|
66
|
-
const pageData = {};
|
|
67
|
-
for (const link of linkHeader.split(',')) {
|
|
68
|
-
const { rel, url } = link.match(/<(?<url>.*)>; rel="(?<rel>\w*)"/)?.groups ?? {};
|
|
69
|
-
pageData[rel] = url;
|
|
70
|
-
}
|
|
71
|
-
return pageData;
|
|
71
|
+
return new URLSearchParams(reducedParams);
|
|
72
72
|
}
|
|
73
|
-
fetch(
|
|
73
|
+
fetch(reqConfig, options) {
|
|
74
74
|
const headers = {
|
|
75
75
|
Authorization: `Bearer ${RotaCloud.config.apiKey}`,
|
|
76
76
|
'SDK-Version': Version.version,
|
|
@@ -88,21 +88,13 @@ export class Service {
|
|
|
88
88
|
}
|
|
89
89
|
else {
|
|
90
90
|
// need to convert user field in payload to a header for creating leave_requests when using an API key
|
|
91
|
-
this.isLeaveRequest(
|
|
91
|
+
this.isLeaveRequest(reqConfig.url) ? (headers.User = `${reqConfig.data.user}`) : undefined;
|
|
92
92
|
}
|
|
93
|
-
const
|
|
94
|
-
...
|
|
93
|
+
const finalReqConfig = {
|
|
94
|
+
...reqConfig,
|
|
95
95
|
baseURL: RotaCloud.config.baseUri,
|
|
96
96
|
headers,
|
|
97
|
-
params:
|
|
98
|
-
expand: options?.expand,
|
|
99
|
-
fields: options?.fields,
|
|
100
|
-
limit: options?.limit,
|
|
101
|
-
offset: options?.offset,
|
|
102
|
-
dry_run: options?.dryRun,
|
|
103
|
-
...httpOptions?.params,
|
|
104
|
-
},
|
|
105
|
-
paramsSerializer: this.buildQueryStr,
|
|
97
|
+
params: this.buildQueryParams(options, reqConfig.params),
|
|
106
98
|
};
|
|
107
99
|
if (RotaCloud.config.retry) {
|
|
108
100
|
const retryConfig = typeof RotaCloud.config.retry === 'string'
|
|
@@ -118,44 +110,29 @@ export class Service {
|
|
|
118
110
|
},
|
|
119
111
|
});
|
|
120
112
|
}
|
|
121
|
-
return this.client.request(
|
|
113
|
+
return this.client.request(finalReqConfig);
|
|
122
114
|
}
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
yield res;
|
|
134
|
-
// Failsafe incase the page does not change
|
|
135
|
-
if (currentPageUrl === pageRequestObject.url) {
|
|
136
|
-
throw new Error('Next page link did not change');
|
|
137
|
-
}
|
|
138
|
-
currentPageUrl = pageRequestObject.url;
|
|
115
|
+
/** Iterates through every page for a potentially paginated request */
|
|
116
|
+
async *fetchPages(reqConfig, options) {
|
|
117
|
+
const fallbackLimit = 20;
|
|
118
|
+
const res = await this.fetch(reqConfig, options);
|
|
119
|
+
yield res;
|
|
120
|
+
const limit = Number(res.headers['x-limit']) || fallbackLimit;
|
|
121
|
+
const entityCount = Number(res.headers['x-total-count']) || 0;
|
|
122
|
+
const requestOffset = Number(res.headers['x-offset']) || 0;
|
|
123
|
+
for (let offset = requestOffset + limit; offset < entityCount; offset += limit) {
|
|
124
|
+
yield this.fetch(reqConfig, { ...options, offset });
|
|
139
125
|
}
|
|
140
126
|
}
|
|
141
|
-
async *listResponses(
|
|
142
|
-
for await (const res of this.
|
|
127
|
+
async *listResponses(reqConfig, options) {
|
|
128
|
+
for await (const res of this.fetchPages(reqConfig, options)) {
|
|
143
129
|
yield* res.data;
|
|
144
130
|
}
|
|
145
131
|
}
|
|
146
|
-
iterator(
|
|
147
|
-
const iterator = this.listResponses(reqObject, options);
|
|
132
|
+
iterator(reqConfig, options) {
|
|
148
133
|
return {
|
|
149
|
-
[Symbol.asyncIterator]()
|
|
150
|
-
|
|
151
|
-
next() {
|
|
152
|
-
return iterator.next();
|
|
153
|
-
},
|
|
154
|
-
};
|
|
155
|
-
},
|
|
156
|
-
byPage: () => {
|
|
157
|
-
return this.listFetch(reqObject, options);
|
|
158
|
-
},
|
|
134
|
+
[Symbol.asyncIterator]: () => this.listResponses(reqConfig, options),
|
|
135
|
+
byPage: () => this.fetchPages(reqConfig, options),
|
|
159
136
|
};
|
|
160
137
|
}
|
|
161
138
|
}
|
|
@@ -3,7 +3,7 @@ import { ApiShift } from '../interfaces/index.js';
|
|
|
3
3
|
import { Service, Options, RequirementsOf } from './index.js';
|
|
4
4
|
import { Shift } from '../models/shift.model.js';
|
|
5
5
|
import { ShiftsQueryParams } from '../interfaces/query-params/shifts-query-params.interface.js';
|
|
6
|
-
|
|
6
|
+
type RequiredProps = 'end_time' | 'start_time' | 'location';
|
|
7
7
|
export declare class ShiftsService extends Service {
|
|
8
8
|
private apiPath;
|
|
9
9
|
create(data: RequirementsOf<ApiShift, RequiredProps>): Promise<Shift>;
|
|
@@ -19,11 +19,33 @@ export declare class ShiftsService extends Service {
|
|
|
19
19
|
list(query: ShiftsQueryParams, options?: Options): AsyncGenerator<Shift, void, unknown>;
|
|
20
20
|
listAll(query: ShiftsQueryParams, options?: Options): Promise<Shift[]>;
|
|
21
21
|
listByPage(query: ShiftsQueryParams, options?: Options): AsyncGenerator<AxiosResponse<ApiShift[], any>, any, unknown>;
|
|
22
|
-
update(
|
|
23
|
-
update(
|
|
22
|
+
update(shift: RequirementsOf<ApiShift, 'id'>): Promise<Shift>;
|
|
23
|
+
update(shift: RequirementsOf<ApiShift, 'id'>, options: {
|
|
24
24
|
rawResponse: true;
|
|
25
|
-
} & Options): Promise<AxiosResponse<ApiShift
|
|
26
|
-
update(
|
|
25
|
+
} & Options): Promise<AxiosResponse<ApiShift>>;
|
|
26
|
+
update(shift: RequirementsOf<ApiShift, 'id'>, options: Options): Promise<Shift>;
|
|
27
|
+
update(shifts: RequirementsOf<ApiShift, 'id'>[]): Promise<{
|
|
28
|
+
success: Shift[];
|
|
29
|
+
failed: {
|
|
30
|
+
id: number;
|
|
31
|
+
error: string;
|
|
32
|
+
}[];
|
|
33
|
+
}>;
|
|
34
|
+
update(shift: RequirementsOf<ApiShift, 'id'>, options: Options): Promise<Shift>;
|
|
35
|
+
update(shifts: RequirementsOf<ApiShift, 'id'>[], options: {
|
|
36
|
+
rawResponse: true;
|
|
37
|
+
} & Options): Promise<AxiosResponse<{
|
|
38
|
+
code: number;
|
|
39
|
+
data?: ApiShift;
|
|
40
|
+
error?: string;
|
|
41
|
+
}[]>>;
|
|
42
|
+
update(shifts: RequirementsOf<ApiShift, 'id'>[], options: Options): Promise<{
|
|
43
|
+
success: Shift[];
|
|
44
|
+
failed: {
|
|
45
|
+
id: number;
|
|
46
|
+
error: string;
|
|
47
|
+
}[];
|
|
48
|
+
}>;
|
|
27
49
|
delete(ids: number | number[]): Promise<number>;
|
|
28
50
|
delete(ids: number | number[], options: {
|
|
29
51
|
rawResponse: true;
|
|
@@ -30,18 +30,40 @@ export class ShiftsService extends Service {
|
|
|
30
30
|
listByPage(query, options) {
|
|
31
31
|
return super.iterator({ url: this.apiPath, params: query }, options).byPage();
|
|
32
32
|
}
|
|
33
|
-
update(
|
|
33
|
+
update(shifts, options) {
|
|
34
|
+
if (!Array.isArray(shifts)) {
|
|
35
|
+
return super
|
|
36
|
+
.fetch({
|
|
37
|
+
url: `${this.apiPath}/${shifts.id}`,
|
|
38
|
+
data: shifts,
|
|
39
|
+
method: 'POST',
|
|
40
|
+
})
|
|
41
|
+
.then((res) => (options?.rawResponse ? res : new Shift(res.data)));
|
|
42
|
+
}
|
|
34
43
|
return super
|
|
35
44
|
.fetch({
|
|
36
|
-
url:
|
|
37
|
-
data,
|
|
45
|
+
url: this.apiPath,
|
|
46
|
+
data: shifts,
|
|
38
47
|
method: 'POST',
|
|
39
48
|
})
|
|
40
|
-
.then((res) =>
|
|
49
|
+
.then((res) => {
|
|
50
|
+
if (options?.rawResponse)
|
|
51
|
+
return res;
|
|
52
|
+
const success = [];
|
|
53
|
+
const failed = [];
|
|
54
|
+
for (let shiftIdx = 0; shiftIdx < res.data.length; shiftIdx += 1) {
|
|
55
|
+
const { data, error } = res.data[shiftIdx];
|
|
56
|
+
if (data)
|
|
57
|
+
success.push(new Shift(data));
|
|
58
|
+
if (error)
|
|
59
|
+
failed.push({ id: shifts[shiftIdx].id, error });
|
|
60
|
+
}
|
|
61
|
+
return { success, failed };
|
|
62
|
+
});
|
|
41
63
|
}
|
|
42
64
|
delete(ids, options) {
|
|
43
|
-
const params =
|
|
44
|
-
? { url: this.apiPath, data: ids, method: 'DELETE' }
|
|
65
|
+
const params = Array.isArray(ids)
|
|
66
|
+
? { url: this.apiPath, data: { ids }, method: 'DELETE' }
|
|
45
67
|
: { url: `${this.apiPath}/${ids}`, method: 'DELETE' };
|
|
46
68
|
return super.fetch(params).then((res) => Promise.resolve(options?.rawResponse ? res : res.status));
|
|
47
69
|
}
|
|
@@ -2,7 +2,7 @@ import { AxiosResponse } from 'axios';
|
|
|
2
2
|
import { ApiTerminal } from '../interfaces/index.js';
|
|
3
3
|
import { Service, Options, RequirementsOf } from './index.js';
|
|
4
4
|
import { Terminal } from '../models/terminal.model.js';
|
|
5
|
-
|
|
5
|
+
type RequiredProps = 'name' | 'timezone';
|
|
6
6
|
declare class TerminalsService extends Service {
|
|
7
7
|
private apiPath;
|
|
8
8
|
create(data: RequirementsOf<ApiTerminal, RequiredProps>): Promise<Terminal>;
|
|
@@ -3,7 +3,7 @@ import { Options, RequirementsOf, Service } from './service';
|
|
|
3
3
|
import { ToilAccrualsQueryParams } from '../interfaces/query-params/toil-accruals-query-params.interface';
|
|
4
4
|
import { ToilAccrual } from '../models/toil-accrual.model';
|
|
5
5
|
import { ApiToilAccrual } from '../interfaces/toil-accrual.interface';
|
|
6
|
-
|
|
6
|
+
type RequiredProps = 'duration_hours' | 'leave_year' | 'user_id';
|
|
7
7
|
export declare class ToilAccrualsService extends Service {
|
|
8
8
|
private apiPath;
|
|
9
9
|
create(data: RequirementsOf<ApiToilAccrual, RequiredProps>): Promise<ToilAccrual>;
|
|
@@ -26,8 +26,8 @@ declare class UserBreak {
|
|
|
26
26
|
end_location: ApiTerminalLocation | null;
|
|
27
27
|
constructor(apiUserBreak: ApiUserBreak);
|
|
28
28
|
}
|
|
29
|
-
|
|
30
|
-
|
|
29
|
+
type RequiredPropsClockIn = 'method';
|
|
30
|
+
type RequiredPropsBreak = 'method' | 'action';
|
|
31
31
|
declare class UsersClockInService extends Service {
|
|
32
32
|
private apiPath;
|
|
33
33
|
getClockedInUser(id: number): Promise<UserClockedIn>;
|
|
@@ -3,7 +3,7 @@ import { ApiUser } from '../interfaces/index.js';
|
|
|
3
3
|
import { Service, Options, RequirementsOf } from './index.js';
|
|
4
4
|
import { User } from '../models/user.model.js';
|
|
5
5
|
import { UsersQueryParams } from '../interfaces/query-params/users-query-params.interface.js';
|
|
6
|
-
|
|
6
|
+
type RequiredProps = 'first_name' | 'last_name';
|
|
7
7
|
declare class UsersService extends Service {
|
|
8
8
|
private apiPath;
|
|
9
9
|
create(data: RequirementsOf<ApiUser, RequiredProps>): Promise<User>;
|
package/dist/mjs/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const Version = { version: '1.0.
|
|
1
|
+
export const Version = { version: '1.0.56' };
|
package/package.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rotacloud",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.56",
|
|
4
4
|
"description": "The RotaCloud SDK for the RotaCloud API",
|
|
5
5
|
"engines": {
|
|
6
|
-
"node": ">=
|
|
6
|
+
"node": ">=16.10.0"
|
|
7
7
|
},
|
|
8
8
|
"main": "./dist/cjs/rotacloud.js",
|
|
9
9
|
"module": "./dist/mjs/rotacloud.js",
|
|
@@ -18,7 +18,8 @@
|
|
|
18
18
|
"watch": "rm -rf dist/* && concurrently \"tsc -p tsconfig.json --watch\" \"tsc -p tsconfig-cjs.json --watch\" && ./fixup",
|
|
19
19
|
"build": "rm -rf dist/* && tsc -p tsconfig.json && tsc -p tsconfig-cjs.json && ./fixup",
|
|
20
20
|
"lint": "eslint src --ext .ts",
|
|
21
|
-
"test": "echo \"Error: no test specified\" && exit 1"
|
|
21
|
+
"test": "echo \"Error: no test specified\" && exit 1",
|
|
22
|
+
"prepublishOnly": "npm run build"
|
|
22
23
|
},
|
|
23
24
|
"repository": {
|
|
24
25
|
"type": "git",
|
|
@@ -31,22 +32,21 @@
|
|
|
31
32
|
},
|
|
32
33
|
"homepage": "https://github.com/rotacloud/rotacloud-node#readme",
|
|
33
34
|
"devDependencies": {
|
|
34
|
-
"@typescript-eslint/eslint-plugin": "^
|
|
35
|
-
"@typescript-eslint/parser": "^
|
|
36
|
-
"concurrently": "^
|
|
37
|
-
"eslint": "^8.
|
|
35
|
+
"@typescript-eslint/eslint-plugin": "^6.2.1",
|
|
36
|
+
"@typescript-eslint/parser": "^6.3.0",
|
|
37
|
+
"concurrently": "^8.2.0",
|
|
38
|
+
"eslint": "^8.46.0",
|
|
38
39
|
"eslint-config-airbnb": "^19.0.0",
|
|
39
40
|
"eslint-config-airbnb-base": "^15.0.0",
|
|
40
|
-
"eslint-config-airbnb-typescript": "^
|
|
41
|
-
"eslint-config-prettier": "^
|
|
42
|
-
"eslint-plugin-import": "^2.
|
|
43
|
-
"
|
|
44
|
-
"
|
|
45
|
-
"typescript": "^4.4.4"
|
|
41
|
+
"eslint-config-airbnb-typescript": "^17.1.0",
|
|
42
|
+
"eslint-config-prettier": "^9.0.0",
|
|
43
|
+
"eslint-plugin-import": "^2.28.0",
|
|
44
|
+
"prettier": "~3.0.1",
|
|
45
|
+
"typescript": "^5.1.6"
|
|
46
46
|
},
|
|
47
47
|
"dependencies": {
|
|
48
|
-
"@types/node": "^16.11.
|
|
49
|
-
"axios": "^
|
|
50
|
-
"axios-retry": "^3.
|
|
48
|
+
"@types/node": "^16.11.7",
|
|
49
|
+
"axios": "^1.4.0",
|
|
50
|
+
"axios-retry": "^3.6.0"
|
|
51
51
|
}
|
|
52
52
|
}
|
|
@@ -26,9 +26,7 @@ export class AttendanceService extends Service {
|
|
|
26
26
|
get(id: number, options: { rawResponse: true } & Options): Promise<AxiosResponse<ApiAttendance, any>>;
|
|
27
27
|
get(id: number, options: Options): Promise<Attendance>;
|
|
28
28
|
get(id: number, options?: Options) {
|
|
29
|
-
return super.fetch<ApiAttendance>({ url: `${this.apiPath}/${id}` }, options).then((res) =>
|
|
30
|
-
return Promise.resolve(options?.rawResponse ? res : new Attendance(res.data));
|
|
31
|
-
});
|
|
29
|
+
return super.fetch<ApiAttendance>({ url: `${this.apiPath}/${id}` }, options).then((res) => Promise.resolve(options?.rawResponse ? res : new Attendance(res.data)));
|
|
32
30
|
}
|
|
33
31
|
|
|
34
32
|
async *list(query: AttendanceQueryParams, options?: Options) {
|
|
@@ -40,13 +40,11 @@ export class AvailabilityService extends Service {
|
|
|
40
40
|
return this.update(
|
|
41
41
|
{
|
|
42
42
|
user,
|
|
43
|
-
dates: dates.map((date) => {
|
|
44
|
-
return {
|
|
43
|
+
dates: dates.map((date) => ({
|
|
45
44
|
date,
|
|
46
45
|
available: [],
|
|
47
46
|
unavailable: [],
|
|
48
|
-
}
|
|
49
|
-
}),
|
|
47
|
+
})),
|
|
50
48
|
},
|
|
51
49
|
options
|
|
52
50
|
);
|
package/src/services/service.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import axios, { AxiosError, AxiosInstance, AxiosRequestConfig,
|
|
1
|
+
import axios, { AxiosError, AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios';
|
|
2
2
|
import axiosRetry from 'axios-retry';
|
|
3
3
|
import { RotaCloud } from '../rotacloud.js';
|
|
4
4
|
import { Version } from '../version.js';
|
|
@@ -27,6 +27,15 @@ export type RetryOptions =
|
|
|
27
27
|
maxRetries: number;
|
|
28
28
|
};
|
|
29
29
|
|
|
30
|
+
export interface Options {
|
|
31
|
+
rawResponse?: boolean;
|
|
32
|
+
expand?: string[];
|
|
33
|
+
fields?: string[];
|
|
34
|
+
limit?: number;
|
|
35
|
+
offset?: number;
|
|
36
|
+
dryRun?: boolean;
|
|
37
|
+
}
|
|
38
|
+
|
|
30
39
|
const DEFAULT_RETRIES = 3;
|
|
31
40
|
const DEFAULT_RETRY_DELAY = 2000;
|
|
32
41
|
|
|
@@ -42,22 +51,6 @@ const DEFAULT_RETRY_STRATEGY_OPTIONS: Record<RetryStrategy, RetryOptions> = {
|
|
|
42
51
|
},
|
|
43
52
|
};
|
|
44
53
|
|
|
45
|
-
export interface Options {
|
|
46
|
-
rawResponse?: boolean;
|
|
47
|
-
expand?: string[];
|
|
48
|
-
fields?: string[];
|
|
49
|
-
limit?: number;
|
|
50
|
-
offset?: number;
|
|
51
|
-
dryRun?: boolean;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
interface PagingObject {
|
|
55
|
-
first: string;
|
|
56
|
-
prev: string;
|
|
57
|
-
next: string;
|
|
58
|
-
last: string;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
54
|
type ParameterPrimitive = string | boolean | number | null;
|
|
62
55
|
type ParameterValue = ParameterPrimitive | ParameterPrimitive[] | undefined;
|
|
63
56
|
|
|
@@ -90,13 +83,22 @@ export abstract class Service<ApiResponse = any> {
|
|
|
90
83
|
return new SDKError(sdkErrorParams);
|
|
91
84
|
}
|
|
92
85
|
|
|
93
|
-
|
|
86
|
+
private isLeaveRequest(endpoint?: string): boolean {
|
|
94
87
|
return endpoint === '/leave_requests';
|
|
95
88
|
}
|
|
96
89
|
|
|
97
|
-
private
|
|
98
|
-
|
|
99
|
-
|
|
90
|
+
private buildQueryParams(options?: Options, extraParams?: Record<string, ParameterValue>) {
|
|
91
|
+
const queryParams: Record<string, ParameterValue> = {
|
|
92
|
+
expand: options?.expand,
|
|
93
|
+
fields: options?.fields,
|
|
94
|
+
limit: options?.limit,
|
|
95
|
+
offset: options?.offset,
|
|
96
|
+
dry_run: options?.dryRun,
|
|
97
|
+
...extraParams,
|
|
98
|
+
// NOTE: Should not overridable so must come after spread of params
|
|
99
|
+
exclude_link_header: true,
|
|
100
|
+
};
|
|
101
|
+
const reducedParams = Object.entries(queryParams ?? {}).reduce((params, [key, val]) => {
|
|
100
102
|
if (val !== undefined && val !== '') {
|
|
101
103
|
if (Array.isArray(val)) params.push(...val.map((item) => [`${key}[]`, String(item)]));
|
|
102
104
|
else params.push([key, String(val)]);
|
|
@@ -104,21 +106,11 @@ export abstract class Service<ApiResponse = any> {
|
|
|
104
106
|
return params;
|
|
105
107
|
}, [] as string[][]);
|
|
106
108
|
|
|
107
|
-
return new URLSearchParams(reducedParams)
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
private parsePageLinkHeader(linkHeader: string): Partial<PagingObject> {
|
|
111
|
-
const pageData = {};
|
|
112
|
-
for (const link of linkHeader.split(',')) {
|
|
113
|
-
const { rel, url } = link.match(/<(?<url>.*)>; rel="(?<rel>\w*)"/)?.groups ?? {};
|
|
114
|
-
pageData[rel] = url;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
return pageData;
|
|
109
|
+
return new URLSearchParams(reducedParams);
|
|
118
110
|
}
|
|
119
111
|
|
|
120
|
-
|
|
121
|
-
const headers:
|
|
112
|
+
fetch<T = ApiResponse>(reqConfig: AxiosRequestConfig, options?: Options): Promise<AxiosResponse<T>> {
|
|
113
|
+
const headers: Record<string, string> = {
|
|
122
114
|
Authorization: `Bearer ${RotaCloud.config.apiKey}`,
|
|
123
115
|
'SDK-Version': Version.version,
|
|
124
116
|
};
|
|
@@ -136,22 +128,14 @@ export abstract class Service<ApiResponse = any> {
|
|
|
136
128
|
headers.Account = String(RotaCloud.config.accountId);
|
|
137
129
|
} else {
|
|
138
130
|
// need to convert user field in payload to a header for creating leave_requests when using an API key
|
|
139
|
-
this.isLeaveRequest(
|
|
131
|
+
this.isLeaveRequest(reqConfig.url) ? (headers.User = `${reqConfig.data.user}`) : undefined;
|
|
140
132
|
}
|
|
141
133
|
|
|
142
|
-
const
|
|
143
|
-
...
|
|
134
|
+
const finalReqConfig: AxiosRequestConfig<T> = {
|
|
135
|
+
...reqConfig,
|
|
144
136
|
baseURL: RotaCloud.config.baseUri,
|
|
145
137
|
headers,
|
|
146
|
-
params:
|
|
147
|
-
expand: options?.expand,
|
|
148
|
-
fields: options?.fields,
|
|
149
|
-
limit: options?.limit,
|
|
150
|
-
offset: options?.offset,
|
|
151
|
-
dry_run: options?.dryRun,
|
|
152
|
-
...httpOptions?.params,
|
|
153
|
-
},
|
|
154
|
-
paramsSerializer: this.buildQueryStr,
|
|
138
|
+
params: this.buildQueryParams(options, reqConfig.params),
|
|
155
139
|
};
|
|
156
140
|
|
|
157
141
|
if (RotaCloud.config.retry) {
|
|
@@ -171,52 +155,37 @@ export abstract class Service<ApiResponse = any> {
|
|
|
171
155
|
});
|
|
172
156
|
}
|
|
173
157
|
|
|
174
|
-
return this.client.request<T>(
|
|
158
|
+
return this.client.request<T>(finalReqConfig);
|
|
175
159
|
}
|
|
176
160
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
yield
|
|
192
|
-
|
|
193
|
-
// Failsafe incase the page does not change
|
|
194
|
-
if (currentPageUrl === pageRequestObject.url) {
|
|
195
|
-
throw new Error('Next page link did not change');
|
|
196
|
-
}
|
|
197
|
-
currentPageUrl = pageRequestObject.url;
|
|
161
|
+
/** Iterates through every page for a potentially paginated request */
|
|
162
|
+
private async *fetchPages<T>(
|
|
163
|
+
reqConfig: AxiosRequestConfig<T[]>,
|
|
164
|
+
options: Options | undefined
|
|
165
|
+
): AsyncGenerator<AxiosResponse<T[]>> {
|
|
166
|
+
const fallbackLimit = 20;
|
|
167
|
+
const res = await this.fetch<T[]>(reqConfig, options);
|
|
168
|
+
yield res;
|
|
169
|
+
|
|
170
|
+
const limit = Number(res.headers['x-limit']) || fallbackLimit;
|
|
171
|
+
const entityCount = Number(res.headers['x-total-count']) || 0;
|
|
172
|
+
const requestOffset = Number(res.headers['x-offset']) || 0;
|
|
173
|
+
|
|
174
|
+
for (let offset = requestOffset + limit; offset < entityCount; offset += limit) {
|
|
175
|
+
yield this.fetch<T[]>(reqConfig, { ...options, offset });
|
|
198
176
|
}
|
|
199
177
|
}
|
|
200
178
|
|
|
201
|
-
private async *listResponses<T = ApiResponse>(
|
|
202
|
-
for await (const res of this.
|
|
179
|
+
private async *listResponses<T = ApiResponse>(reqConfig: AxiosRequestConfig<T[]>, options?: Options) {
|
|
180
|
+
for await (const res of this.fetchPages<T>(reqConfig, options)) {
|
|
203
181
|
yield* res.data;
|
|
204
182
|
}
|
|
205
183
|
}
|
|
206
184
|
|
|
207
|
-
|
|
208
|
-
const iterator = this.listResponses<T>(reqObject, options);
|
|
185
|
+
iterator<T = ApiResponse>(reqConfig: AxiosRequestConfig<T[]>, options?: Options) {
|
|
209
186
|
return {
|
|
210
|
-
[Symbol.asyncIterator]()
|
|
211
|
-
|
|
212
|
-
next() {
|
|
213
|
-
return iterator.next();
|
|
214
|
-
},
|
|
215
|
-
};
|
|
216
|
-
},
|
|
217
|
-
byPage: () => {
|
|
218
|
-
return this.listFetch<T>(reqObject, options);
|
|
219
|
-
},
|
|
187
|
+
[Symbol.asyncIterator]: () => this.listResponses<T>(reqConfig, options),
|
|
188
|
+
byPage: () => this.fetchPages<T>(reqConfig, options),
|
|
220
189
|
};
|
|
221
190
|
}
|
|
222
191
|
}
|
|
@@ -51,31 +51,62 @@ export class ShiftsService extends Service {
|
|
|
51
51
|
return super.iterator<ApiShift>({ url: this.apiPath, params: query }, options).byPage();
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
-
update(
|
|
54
|
+
update(shift: RequirementsOf<ApiShift, 'id'>): Promise<Shift>;
|
|
55
55
|
update(
|
|
56
|
-
|
|
57
|
-
data: Partial<ApiShift>,
|
|
56
|
+
shift: RequirementsOf<ApiShift, 'id'>,
|
|
58
57
|
options: { rawResponse: true } & Options
|
|
59
|
-
): Promise<AxiosResponse<ApiShift
|
|
60
|
-
update(
|
|
61
|
-
update(
|
|
58
|
+
): Promise<AxiosResponse<ApiShift>>;
|
|
59
|
+
update(shift: RequirementsOf<ApiShift, 'id'>, options: Options): Promise<Shift>;
|
|
60
|
+
update(
|
|
61
|
+
shifts: RequirementsOf<ApiShift, 'id'>[]
|
|
62
|
+
): Promise<{ success: Shift[]; failed: { id: number; error: string }[] }>;
|
|
63
|
+
update(shift: RequirementsOf<ApiShift, 'id'>, options: Options): Promise<Shift>;
|
|
64
|
+
update(
|
|
65
|
+
shifts: RequirementsOf<ApiShift, 'id'>[],
|
|
66
|
+
options: { rawResponse: true } & Options
|
|
67
|
+
): Promise<AxiosResponse<{ code: number; data?: ApiShift; error?: string }[]>>;
|
|
68
|
+
update(
|
|
69
|
+
shifts: RequirementsOf<ApiShift, 'id'>[],
|
|
70
|
+
options: Options
|
|
71
|
+
): Promise<{ success: Shift[]; failed: { id: number; error: string }[] }>;
|
|
72
|
+
update(shifts: RequirementsOf<ApiShift, 'id'> | RequirementsOf<ApiShift, 'id'>[], options?: Options) {
|
|
73
|
+
if (!Array.isArray(shifts)) {
|
|
74
|
+
return super
|
|
75
|
+
.fetch<ApiShift>({
|
|
76
|
+
url: `${this.apiPath}/${shifts.id}`,
|
|
77
|
+
data: shifts,
|
|
78
|
+
method: 'POST',
|
|
79
|
+
})
|
|
80
|
+
.then((res) => (options?.rawResponse ? res : new Shift(res.data)));
|
|
81
|
+
}
|
|
82
|
+
|
|
62
83
|
return super
|
|
63
|
-
.fetch<ApiShift>({
|
|
64
|
-
url:
|
|
65
|
-
data,
|
|
84
|
+
.fetch<{ code: number; data?: ApiShift; error?: string }[]>({
|
|
85
|
+
url: this.apiPath,
|
|
86
|
+
data: shifts,
|
|
66
87
|
method: 'POST',
|
|
67
88
|
})
|
|
68
|
-
.then((res) =>
|
|
89
|
+
.then((res) => {
|
|
90
|
+
if (options?.rawResponse) return res;
|
|
91
|
+
|
|
92
|
+
const success: Shift[] = [];
|
|
93
|
+
const failed: { id: number; error: string }[] = [];
|
|
94
|
+
for (let shiftIdx = 0; shiftIdx < res.data.length; shiftIdx += 1) {
|
|
95
|
+
const { data, error } = res.data[shiftIdx];
|
|
96
|
+
if (data) success.push(new Shift(data));
|
|
97
|
+
if (error) failed.push({ id: shifts[shiftIdx].id, error });
|
|
98
|
+
}
|
|
99
|
+
return { success, failed };
|
|
100
|
+
});
|
|
69
101
|
}
|
|
70
102
|
|
|
71
103
|
delete(ids: number | number[]): Promise<number>;
|
|
72
104
|
delete(ids: number | number[], options: { rawResponse: true } & Options): Promise<AxiosResponse<any, any>>;
|
|
73
105
|
delete(ids: number | number[], options: Options): Promise<number>;
|
|
74
106
|
delete(ids: number | number[], options?: Options) {
|
|
75
|
-
const params: AxiosRequestConfig =
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
: { url: `${this.apiPath}/${ids}`, method: 'DELETE' };
|
|
107
|
+
const params: AxiosRequestConfig = Array.isArray(ids)
|
|
108
|
+
? { url: this.apiPath, data: { ids }, method: 'DELETE' }
|
|
109
|
+
: { url: `${this.apiPath}/${ids}`, method: 'DELETE' };
|
|
79
110
|
|
|
80
111
|
return super.fetch<ApiShift>(params).then((res) => Promise.resolve(options?.rawResponse ? res : res.status));
|
|
81
112
|
}
|
package/src/version.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const Version = { version: '1.0.
|
|
1
|
+
export const Version = { version: '1.0.56' };
|