@slotchain/sdk 1.1.4 → 1.2.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/dist/index.cjs.js +182 -14
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.mts +261 -21
- package/dist/index.d.ts +261 -21
- package/dist/index.esm.js +182 -14
- package/dist/index.esm.js.map +1 -1
- package/package.json +1 -2
- package/src/clients/customer-client.ts +129 -29
- package/src/clients/customer-client.ts.bak +165 -0
- package/src/clients/document-client.ts +148 -0
- package/src/index.ts +16 -0
- package/src/types/api.ts +150 -1
- package/src/types/next-ambient.d.ts +29 -0
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
import { AxiosInstance } from 'axios';
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
ApiResponse,
|
|
4
|
+
Customer,
|
|
5
|
+
CustomerIdentity,
|
|
6
|
+
CustomerWithIdentities,
|
|
7
|
+
CustomerWithBookings,
|
|
8
|
+
NotificationPreferences,
|
|
9
|
+
FindOrCreateCustomerOptions,
|
|
10
|
+
ResolveCustomerOptions,
|
|
11
|
+
} from '../types/api';
|
|
3
12
|
|
|
4
13
|
export class CustomerClient {
|
|
5
14
|
constructor(private client: AxiosInstance) {}
|
|
@@ -58,11 +67,8 @@ export class CustomerClient {
|
|
|
58
67
|
/**
|
|
59
68
|
* Create a new customer
|
|
60
69
|
* POST /api/v1/customers
|
|
61
|
-
*
|
|
62
|
-
* @param data - Customer creation data (tenant_id, name, email required)
|
|
63
|
-
* @returns Created customer
|
|
64
70
|
*/
|
|
65
|
-
async create(data: Partial<Customer>): Promise<ApiResponse<Customer>> {
|
|
71
|
+
async create(data: Partial<Customer> & { tenant_id: string; name: string; email: string }): Promise<ApiResponse<Customer>> {
|
|
66
72
|
const response = await this.client.post<ApiResponse<Customer>>(
|
|
67
73
|
'/api/v1/customers',
|
|
68
74
|
data
|
|
@@ -119,14 +125,25 @@ export class CustomerClient {
|
|
|
119
125
|
}
|
|
120
126
|
|
|
121
127
|
/**
|
|
122
|
-
* Get autocomplete data from existing customers and bookings
|
|
123
|
-
* GET /api/v1/customers/autocomplete
|
|
124
|
-
*
|
|
125
|
-
* @param tenantId - Tenant ID (required)
|
|
126
|
-
* @returns Autocomplete data
|
|
128
|
+
* Get autocomplete data from existing customers and bookings.
|
|
129
|
+
* GET /api/v1/customers/autocomplete?tenantId=:tenantId
|
|
127
130
|
*/
|
|
128
|
-
async autocomplete(tenantId: string): Promise<ApiResponse<
|
|
129
|
-
|
|
131
|
+
async autocomplete(tenantId: string): Promise<ApiResponse<{
|
|
132
|
+
companies: string[];
|
|
133
|
+
jobTitles: string[];
|
|
134
|
+
experienceLevels: string[];
|
|
135
|
+
cities: string[];
|
|
136
|
+
states: string[];
|
|
137
|
+
zipCodes: string[];
|
|
138
|
+
}>> {
|
|
139
|
+
const response = await this.client.get<ApiResponse<{
|
|
140
|
+
companies: string[];
|
|
141
|
+
jobTitles: string[];
|
|
142
|
+
experienceLevels: string[];
|
|
143
|
+
cities: string[];
|
|
144
|
+
states: string[];
|
|
145
|
+
zipCodes: string[];
|
|
146
|
+
}>>(
|
|
130
147
|
'/api/v1/customers/autocomplete',
|
|
131
148
|
{ params: { tenantId } }
|
|
132
149
|
);
|
|
@@ -134,32 +151,115 @@ export class CustomerClient {
|
|
|
134
151
|
}
|
|
135
152
|
|
|
136
153
|
/**
|
|
137
|
-
* Get all bookings for a customer
|
|
138
|
-
* GET /api/v1/customers/bookings
|
|
139
|
-
*
|
|
140
|
-
* @param params - Query parameters (email optional, userId optional, tenantId required)
|
|
141
|
-
* @returns Customer bookings data
|
|
154
|
+
* Get all bookings for a customer, grouped by slot.
|
|
155
|
+
* GET /api/v1/customers/bookings?tenantId=:id
|
|
142
156
|
*/
|
|
143
157
|
async getBookings(params: {
|
|
144
158
|
email?: string;
|
|
145
159
|
userId?: string;
|
|
146
160
|
tenantId: string;
|
|
147
|
-
}): Promise<ApiResponse<{
|
|
148
|
-
|
|
149
|
-
bookings: Booking[];
|
|
150
|
-
groupedBySlot: Record<string, Booking[]>;
|
|
151
|
-
total: number;
|
|
152
|
-
}>> {
|
|
153
|
-
const response = await this.client.get<ApiResponse<{
|
|
154
|
-
customer: Customer | null;
|
|
155
|
-
bookings: Booking[];
|
|
156
|
-
groupedBySlot: Record<string, Booking[]>;
|
|
157
|
-
total: number;
|
|
158
|
-
}>>(
|
|
161
|
+
}): Promise<ApiResponse<CustomerWithBookings>> {
|
|
162
|
+
const response = await this.client.get<ApiResponse<CustomerWithBookings>>(
|
|
159
163
|
'/api/v1/customers/bookings',
|
|
160
164
|
{ params }
|
|
161
165
|
);
|
|
162
166
|
return response.data;
|
|
163
167
|
}
|
|
168
|
+
|
|
169
|
+
// ─── Atomic upsert ────────────────────────────────────────────────────────
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Atomic upsert: find an existing customer by email within a tenant, or create one.
|
|
173
|
+
* POST /api/v1/customers/find-or-create
|
|
174
|
+
*/
|
|
175
|
+
async findOrCreate(options: FindOrCreateCustomerOptions): Promise<ApiResponse<Customer & { created: boolean }>> {
|
|
176
|
+
const response = await this.client.post<ApiResponse<Customer & { created: boolean }>>(
|
|
177
|
+
'/api/v1/customers/find-or-create',
|
|
178
|
+
options
|
|
179
|
+
);
|
|
180
|
+
return response.data;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Resolve a customer from an IDP identity or email.
|
|
185
|
+
* Tries idpId lookup (customer_users) first, then falls back to email.
|
|
186
|
+
* POST /api/v1/customers/resolve
|
|
187
|
+
*/
|
|
188
|
+
async resolve(options: ResolveCustomerOptions): Promise<ApiResponse<Customer | null>> {
|
|
189
|
+
const response = await this.client.post<ApiResponse<Customer | null>>(
|
|
190
|
+
'/api/v1/customers/resolve',
|
|
191
|
+
options
|
|
192
|
+
);
|
|
193
|
+
return response.data;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// ─── IDP Identity Management ─────────────────────────────────────────────
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Get a customer with their linked IDP identities (customer_users rows).
|
|
200
|
+
* GET /api/v1/customers/:id?include_identities=true
|
|
201
|
+
*/
|
|
202
|
+
async getWithIdentities(id: string): Promise<ApiResponse<CustomerWithIdentities>> {
|
|
203
|
+
const response = await this.client.get<ApiResponse<CustomerWithIdentities>>(
|
|
204
|
+
`/api/v1/customers/${id}`,
|
|
205
|
+
{ params: { include_identities: true } }
|
|
206
|
+
);
|
|
207
|
+
return response.data;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Link an IDP user ID to a customer (creates a customer_users row).
|
|
212
|
+
* POST /api/v1/customers/:id/users
|
|
213
|
+
*/
|
|
214
|
+
async linkUser(
|
|
215
|
+
customerId: string,
|
|
216
|
+
idpId: string,
|
|
217
|
+
options?: { role?: CustomerIdentity['role']; is_primary?: boolean }
|
|
218
|
+
): Promise<ApiResponse<CustomerIdentity>> {
|
|
219
|
+
const response = await this.client.post<ApiResponse<CustomerIdentity>>(
|
|
220
|
+
`/api/v1/customers/${customerId}/users`,
|
|
221
|
+
{ idp_id: idpId, role: options?.role ?? 'member', is_primary: options?.is_primary ?? false }
|
|
222
|
+
);
|
|
223
|
+
return response.data;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Unlink an IDP user from a customer (deletes the customer_users row).
|
|
228
|
+
* DELETE /api/v1/customers/:id/users/:idpId
|
|
229
|
+
*/
|
|
230
|
+
async unlinkUser(customerId: string, idpId: string): Promise<ApiResponse<void>> {
|
|
231
|
+
const response = await this.client.delete<ApiResponse<void>>(
|
|
232
|
+
`/api/v1/customers/${customerId}/users/${encodeURIComponent(idpId)}`
|
|
233
|
+
);
|
|
234
|
+
return response.data;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// ─── Notification Preferences ────────────────────────────────────────────
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Get notification preferences for a customer.
|
|
241
|
+
* GET /api/v1/customers/:id/preferences/notifications
|
|
242
|
+
*/
|
|
243
|
+
async getNotificationPreferences(customerId: string): Promise<ApiResponse<NotificationPreferences>> {
|
|
244
|
+
const response = await this.client.get<ApiResponse<NotificationPreferences>>(
|
|
245
|
+
`/api/v1/customers/${customerId}/preferences/notifications`
|
|
246
|
+
);
|
|
247
|
+
return response.data;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Update notification preferences for a customer (partial merge).
|
|
252
|
+
* PATCH /api/v1/customers/:id/preferences/notifications
|
|
253
|
+
*/
|
|
254
|
+
async updateNotificationPreferences(
|
|
255
|
+
customerId: string,
|
|
256
|
+
preferences: Partial<NotificationPreferences>
|
|
257
|
+
): Promise<ApiResponse<NotificationPreferences>> {
|
|
258
|
+
const response = await this.client.patch<ApiResponse<NotificationPreferences>>(
|
|
259
|
+
`/api/v1/customers/${customerId}/preferences/notifications`,
|
|
260
|
+
preferences
|
|
261
|
+
);
|
|
262
|
+
return response.data;
|
|
263
|
+
}
|
|
164
264
|
}
|
|
165
265
|
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import { AxiosInstance } from 'axios';
|
|
2
|
+
import { ApiResponse, Customer, Booking } from '../types/api';
|
|
3
|
+
|
|
4
|
+
export class CustomerClient {
|
|
5
|
+
constructor(private client: AxiosInstance) {}
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Get customer by ID
|
|
9
|
+
* GET /api/v1/customers/:id
|
|
10
|
+
*
|
|
11
|
+
* @param id - Customer ID
|
|
12
|
+
* @returns Customer details
|
|
13
|
+
*/
|
|
14
|
+
async getById(id: string): Promise<ApiResponse<Customer>> {
|
|
15
|
+
const response = await this.client.get<ApiResponse<Customer>>(
|
|
16
|
+
`/api/v1/customers/${id}`
|
|
17
|
+
);
|
|
18
|
+
return response.data;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Get customer by email
|
|
23
|
+
* GET /api/v1/customers?email=:email&tenant_id=:id
|
|
24
|
+
*
|
|
25
|
+
* @param email - Customer email
|
|
26
|
+
* @param tenantId - Required tenant ID for scoping
|
|
27
|
+
* @returns Customer details
|
|
28
|
+
*/
|
|
29
|
+
async getByEmail(email: string, tenantId: string): Promise<ApiResponse<Customer>> {
|
|
30
|
+
const response = await this.client.get<ApiResponse<Customer>>(
|
|
31
|
+
'/api/v1/customers',
|
|
32
|
+
{ params: { email, tenant_id: tenantId } }
|
|
33
|
+
);
|
|
34
|
+
return response.data;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* List customers with optional filters
|
|
39
|
+
* GET /api/v1/customers
|
|
40
|
+
*
|
|
41
|
+
* @param params - Query parameters (tenant_id required, page, limit, search, is_guest)
|
|
42
|
+
* @returns Paginated list of customers
|
|
43
|
+
*/
|
|
44
|
+
async list(params: {
|
|
45
|
+
tenant_id: string;
|
|
46
|
+
page?: number;
|
|
47
|
+
limit?: number;
|
|
48
|
+
search?: string;
|
|
49
|
+
is_guest?: boolean;
|
|
50
|
+
}): Promise<ApiResponse<Customer[]>> {
|
|
51
|
+
const response = await this.client.get<ApiResponse<Customer[]>>(
|
|
52
|
+
'/api/v1/customers',
|
|
53
|
+
{ params }
|
|
54
|
+
);
|
|
55
|
+
return response.data;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Create a new customer
|
|
60
|
+
* POST /api/v1/customers
|
|
61
|
+
*
|
|
62
|
+
* @param data - Customer creation data (tenant_id, name, email required)
|
|
63
|
+
* @returns Created customer
|
|
64
|
+
*/
|
|
65
|
+
async create(data: Partial<Customer>): Promise<ApiResponse<Customer>> {
|
|
66
|
+
const response = await this.client.post<ApiResponse<Customer>>(
|
|
67
|
+
'/api/v1/customers',
|
|
68
|
+
data
|
|
69
|
+
);
|
|
70
|
+
return response.data;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Update customer by ID
|
|
75
|
+
* PUT /api/v1/customers/:id
|
|
76
|
+
*
|
|
77
|
+
* @param id - Customer ID
|
|
78
|
+
* @param data - Customer update data
|
|
79
|
+
* @returns Updated customer
|
|
80
|
+
*/
|
|
81
|
+
async update(id: string, data: Partial<Customer>): Promise<ApiResponse<Customer>> {
|
|
82
|
+
const response = await this.client.put<ApiResponse<Customer>>(
|
|
83
|
+
`/api/v1/customers/${id}`,
|
|
84
|
+
data
|
|
85
|
+
);
|
|
86
|
+
return response.data;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Delete customer by ID
|
|
91
|
+
* DELETE /api/v1/customers/:id
|
|
92
|
+
*
|
|
93
|
+
* @param id - Customer ID
|
|
94
|
+
* @returns Empty response on success
|
|
95
|
+
*/
|
|
96
|
+
async delete(id: string): Promise<ApiResponse<void>> {
|
|
97
|
+
const response = await this.client.delete<ApiResponse<void>>(
|
|
98
|
+
`/api/v1/customers/${id}`
|
|
99
|
+
);
|
|
100
|
+
return response.data;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Lookup customer by userId or tenantId
|
|
105
|
+
* GET /api/v1/customers/lookup
|
|
106
|
+
*
|
|
107
|
+
* @param params - Query parameters (userId optional, tenantId required)
|
|
108
|
+
* @returns Customer or null
|
|
109
|
+
*/
|
|
110
|
+
async lookup(params: {
|
|
111
|
+
userId?: string;
|
|
112
|
+
tenantId: string;
|
|
113
|
+
}): Promise<ApiResponse<Customer | null>> {
|
|
114
|
+
const response = await this.client.get<ApiResponse<Customer | null>>(
|
|
115
|
+
'/api/v1/customers/lookup',
|
|
116
|
+
{ params: { userId: params.userId, tenantId: params.tenantId } }
|
|
117
|
+
);
|
|
118
|
+
return response.data;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Get autocomplete data from existing customers and bookings
|
|
123
|
+
* GET /api/v1/customers/autocomplete
|
|
124
|
+
*
|
|
125
|
+
* @param tenantId - Tenant ID (required)
|
|
126
|
+
* @returns Autocomplete data
|
|
127
|
+
*/
|
|
128
|
+
async autocomplete(tenantId: string): Promise<ApiResponse<any>> {
|
|
129
|
+
const response = await this.client.get<ApiResponse<any>>(
|
|
130
|
+
'/api/v1/customers/autocomplete',
|
|
131
|
+
{ params: { tenantId } }
|
|
132
|
+
);
|
|
133
|
+
return response.data;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Get all bookings for a customer
|
|
138
|
+
* GET /api/v1/customers/bookings
|
|
139
|
+
*
|
|
140
|
+
* @param params - Query parameters (email optional, userId optional, tenantId required)
|
|
141
|
+
* @returns Customer bookings data
|
|
142
|
+
*/
|
|
143
|
+
async getBookings(params: {
|
|
144
|
+
email?: string;
|
|
145
|
+
userId?: string;
|
|
146
|
+
tenantId: string;
|
|
147
|
+
}): Promise<ApiResponse<{
|
|
148
|
+
customer: Customer | null;
|
|
149
|
+
bookings: Booking[];
|
|
150
|
+
groupedBySlot: Record<string, Booking[]>;
|
|
151
|
+
total: number;
|
|
152
|
+
}>> {
|
|
153
|
+
const response = await this.client.get<ApiResponse<{
|
|
154
|
+
customer: Customer | null;
|
|
155
|
+
bookings: Booking[];
|
|
156
|
+
groupedBySlot: Record<string, Booking[]>;
|
|
157
|
+
total: number;
|
|
158
|
+
}>>(
|
|
159
|
+
'/api/v1/customers/bookings',
|
|
160
|
+
{ params }
|
|
161
|
+
);
|
|
162
|
+
return response.data;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import type { AxiosInstance } from 'axios';
|
|
2
|
+
import type {
|
|
3
|
+
ApiResponse,
|
|
4
|
+
Artifact,
|
|
5
|
+
ArtifactWithUrl,
|
|
6
|
+
ListArtifactsOptions,
|
|
7
|
+
UploadArtifactOptions,
|
|
8
|
+
ArtifactEntityType,
|
|
9
|
+
} from '../types/api';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* DocumentClient — manage file artifacts attached to Slotly entities.
|
|
13
|
+
*
|
|
14
|
+
* Artifacts are tenant-scoped files (CVs, invoices, images, etc.) linked
|
|
15
|
+
* to a customer, booking, slot, service, or tenant record.
|
|
16
|
+
*
|
|
17
|
+
* @example Upload a CV for a customer
|
|
18
|
+
* ```ts
|
|
19
|
+
* const artifact = await slotly.document.upload({
|
|
20
|
+
* file: buffer,
|
|
21
|
+
* filename: 'cv-2024.pdf',
|
|
22
|
+
* related_entity_type: 'customer',
|
|
23
|
+
* related_entity_id: customerId,
|
|
24
|
+
* label: 'CV',
|
|
25
|
+
* });
|
|
26
|
+
* ```
|
|
27
|
+
*
|
|
28
|
+
* @example List all CVs for a customer
|
|
29
|
+
* ```ts
|
|
30
|
+
* const { data } = await slotly.document.listByEntity('customer', customerId, { signed: true });
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
export class DocumentClient {
|
|
34
|
+
private readonly client: AxiosInstance;
|
|
35
|
+
private readonly base = '/api/v1/artifacts';
|
|
36
|
+
|
|
37
|
+
constructor(client: AxiosInstance) {
|
|
38
|
+
this.client = client;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Upload a file artifact.
|
|
43
|
+
* Sends as multipart/form-data.
|
|
44
|
+
*/
|
|
45
|
+
async upload(options: UploadArtifactOptions): Promise<Artifact> {
|
|
46
|
+
const form = new FormData();
|
|
47
|
+
|
|
48
|
+
// Normalise file to Blob for FormData compatibility
|
|
49
|
+
let blob: Blob;
|
|
50
|
+
if (options.file instanceof Blob) {
|
|
51
|
+
blob = options.file;
|
|
52
|
+
} else {
|
|
53
|
+
blob = new Blob([options.file], { type: options.mime_type ?? 'application/octet-stream' });
|
|
54
|
+
}
|
|
55
|
+
form.append('file', blob, options.filename);
|
|
56
|
+
form.append('related_entity_type', options.related_entity_type);
|
|
57
|
+
form.append('related_entity_id', options.related_entity_id);
|
|
58
|
+
if (options.label) form.append('label', options.label);
|
|
59
|
+
if (options.mime_type) form.append('mime_type', options.mime_type);
|
|
60
|
+
if (options.metadata) form.append('metadata', JSON.stringify(options.metadata));
|
|
61
|
+
|
|
62
|
+
const response = await this.client.post<ApiResponse<Artifact>>(this.base, form, {
|
|
63
|
+
headers: { 'Content-Type': 'multipart/form-data' },
|
|
64
|
+
});
|
|
65
|
+
const { data } = response.data;
|
|
66
|
+
if (!data) throw new Error('No artifact returned from upload');
|
|
67
|
+
return data;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* List artifacts for a specific entity.
|
|
72
|
+
* Pass `signed: true` to get download URLs in each result.
|
|
73
|
+
*/
|
|
74
|
+
async listByEntity(
|
|
75
|
+
entityType: ArtifactEntityType,
|
|
76
|
+
entityId: string,
|
|
77
|
+
options?: Omit<ListArtifactsOptions, 'related_entity_type' | 'related_entity_id'>
|
|
78
|
+
): Promise<ApiResponse<(Artifact | ArtifactWithUrl)[]>> {
|
|
79
|
+
const response = await this.client.get<ApiResponse<(Artifact | ArtifactWithUrl)[]>>(
|
|
80
|
+
this.base,
|
|
81
|
+
{
|
|
82
|
+
params: {
|
|
83
|
+
related_entity_type: entityType,
|
|
84
|
+
related_entity_id: entityId,
|
|
85
|
+
...options,
|
|
86
|
+
},
|
|
87
|
+
}
|
|
88
|
+
);
|
|
89
|
+
return response.data;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* List artifacts with full filter options.
|
|
94
|
+
*/
|
|
95
|
+
async list(
|
|
96
|
+
options: ListArtifactsOptions
|
|
97
|
+
): Promise<ApiResponse<(Artifact | ArtifactWithUrl)[]>> {
|
|
98
|
+
const response = await this.client.get<ApiResponse<(Artifact | ArtifactWithUrl)[]>>(
|
|
99
|
+
this.base,
|
|
100
|
+
{ params: options }
|
|
101
|
+
);
|
|
102
|
+
return response.data;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Get a single artifact by ID.
|
|
107
|
+
*/
|
|
108
|
+
async get(id: string, signed = false): Promise<Artifact | ArtifactWithUrl> {
|
|
109
|
+
const response = await this.client.get<ApiResponse<Artifact | ArtifactWithUrl>>(
|
|
110
|
+
`${this.base}/${id}`,
|
|
111
|
+
{ params: signed ? { signed: true } : undefined }
|
|
112
|
+
);
|
|
113
|
+
const { data } = response.data;
|
|
114
|
+
if (!data) throw new Error(`Artifact ${id} not found`);
|
|
115
|
+
return data;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Get a signed download URL for an artifact.
|
|
120
|
+
* Returns the full ArtifactWithUrl including `signed_url` and `signed_url_expires_at`.
|
|
121
|
+
*/
|
|
122
|
+
async getUrl(id: string): Promise<ArtifactWithUrl> {
|
|
123
|
+
return this.get(id, true) as Promise<ArtifactWithUrl>;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Delete an artifact and its stored file.
|
|
128
|
+
*/
|
|
129
|
+
async delete(id: string): Promise<void> {
|
|
130
|
+
await this.client.delete(`${this.base}/${id}`);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Update artifact label or metadata.
|
|
135
|
+
*/
|
|
136
|
+
async update(
|
|
137
|
+
id: string,
|
|
138
|
+
patch: { label?: string; metadata?: Record<string, unknown> }
|
|
139
|
+
): Promise<Artifact> {
|
|
140
|
+
const response = await this.client.patch<ApiResponse<Artifact>>(
|
|
141
|
+
`${this.base}/${id}`,
|
|
142
|
+
patch
|
|
143
|
+
);
|
|
144
|
+
const { data } = response.data;
|
|
145
|
+
if (!data) throw new Error(`Artifact ${id} not found`);
|
|
146
|
+
return data;
|
|
147
|
+
}
|
|
148
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -10,6 +10,7 @@ import { StudioClient } from './clients/studio-client';
|
|
|
10
10
|
import { NotificationClient } from './clients/notification-client';
|
|
11
11
|
import { DataQualityClient } from './clients/data-quality-client';
|
|
12
12
|
import { OrganizationClient } from './clients/organization-client';
|
|
13
|
+
import { DocumentClient } from './clients/document-client';
|
|
13
14
|
import { setupRequestInterceptor, setupResponseInterceptor, setupErrorInterceptor } from './interceptors';
|
|
14
15
|
import { setupRetryInterceptor } from './retry';
|
|
15
16
|
import { SlotlyConfigurationError } from './errors';
|
|
@@ -31,6 +32,12 @@ export type {
|
|
|
31
32
|
Category,
|
|
32
33
|
Slot,
|
|
33
34
|
Customer,
|
|
35
|
+
CustomerIdentity,
|
|
36
|
+
CustomerWithIdentities,
|
|
37
|
+
NotificationPreferences,
|
|
38
|
+
CustomerWithBookings,
|
|
39
|
+
ResolveCustomerOptions,
|
|
40
|
+
FindOrCreateCustomerOptions,
|
|
34
41
|
Flow,
|
|
35
42
|
FlowStep,
|
|
36
43
|
Studio,
|
|
@@ -39,6 +46,12 @@ export type {
|
|
|
39
46
|
Organization,
|
|
40
47
|
OrganizationFullConfig,
|
|
41
48
|
GetOrganizationOptions,
|
|
49
|
+
Artifact,
|
|
50
|
+
ArtifactWithUrl,
|
|
51
|
+
ArtifactEntityType,
|
|
52
|
+
ArtifactMimeCategory,
|
|
53
|
+
ListArtifactsOptions,
|
|
54
|
+
UploadArtifactOptions,
|
|
42
55
|
} from './types/api';
|
|
43
56
|
|
|
44
57
|
// Re-export errors
|
|
@@ -105,6 +118,8 @@ export interface SlotlyApi {
|
|
|
105
118
|
notification: NotificationClient;
|
|
106
119
|
dataQuality: DataQualityClient;
|
|
107
120
|
organization: OrganizationClient;
|
|
121
|
+
/** Upload and retrieve file artifacts (CVs, invoices, images) linked to any Slotly entity */
|
|
122
|
+
document: DocumentClient;
|
|
108
123
|
}
|
|
109
124
|
|
|
110
125
|
const defaultBaseURL = typeof process !== 'undefined' && process.env?.SLOTLY_API_URL
|
|
@@ -298,6 +313,7 @@ export const useSlotly = (options: SlotlyClientOptions): SlotlyApi => {
|
|
|
298
313
|
notification: new NotificationClient(client),
|
|
299
314
|
dataQuality: new DataQualityClient(client),
|
|
300
315
|
organization: new OrganizationClient(client),
|
|
316
|
+
document: new DocumentClient(client),
|
|
301
317
|
};
|
|
302
318
|
};
|
|
303
319
|
|