weflayr 0.22.1 → 0.23.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/api/generated/facade.gen.d.ts +64 -94
- package/dist/api/generated/facade.gen.js +8 -10
- package/dist/api/generated/index.d.ts +2 -2
- package/dist/api/generated/index.js +8 -10
- package/dist/api/generated/sdk.gen.d.ts +19 -27
- package/dist/api/generated/sdk.gen.js +36 -54
- package/dist/api/generated/types.gen.d.ts +204 -166
- package/dist/api/index.d.ts +1 -1
- package/dist/api/index.js +1 -1
- package/package.json +1 -1
- package/scripts/generate_facade.mjs +2 -2
- package/src/api/generated/facade.gen.ts +37 -49
- package/src/api/generated/index.ts +2 -2
- package/src/api/generated/sdk.gen.ts +31 -49
- package/src/api/generated/types.gen.ts +211 -181
- package/src/api/index.ts +1 -1
- package/tests/api/index.test.js +176 -69
package/tests/api/index.test.js
CHANGED
|
@@ -48,7 +48,7 @@ async function withClientReturning(responseBody, statusCode, run, clientOptions
|
|
|
48
48
|
|
|
49
49
|
test('client sends the bearer key to the configured host', async () => {
|
|
50
50
|
await withClientReturning({ rows: [] }, 200, async (client, seenRequests) => {
|
|
51
|
-
const { data } = await api.
|
|
51
|
+
const { data } = await api.getRevenue({ client, throwOnError: true });
|
|
52
52
|
assert.deepEqual(data, { rows: [] });
|
|
53
53
|
assert.deepEqual(seenRequests, [
|
|
54
54
|
{ method: 'GET', url: '/api/revenue/', authorization: 'Bearer wf-test-key', body: null },
|
|
@@ -82,7 +82,7 @@ test('client picks up apiKey from WEFLAYR_API_KEY env var', async () => {
|
|
|
82
82
|
{ rows: [] },
|
|
83
83
|
200,
|
|
84
84
|
async (client, seenRequests) => {
|
|
85
|
-
await api.
|
|
85
|
+
await api.getRevenue({ client, throwOnError: true });
|
|
86
86
|
assert.equal(seenRequests[0].authorization, 'Bearer env-key');
|
|
87
87
|
},
|
|
88
88
|
{ apiKey: undefined }
|
|
@@ -97,8 +97,8 @@ test('client picks up apiKey from WEFLAYR_API_KEY env var', async () => {
|
|
|
97
97
|
test('setRevenue posts the rows in snake_case and parses the booked count', async () => {
|
|
98
98
|
await withClientReturning({ ok: true, rows_inserted: 2 }, 200, async (client, seenRequests) => {
|
|
99
99
|
const rows = [
|
|
100
|
-
{ customerName: 'c_1', amount:
|
|
101
|
-
{ customerName: 'c_2', amount:
|
|
100
|
+
{ customerName: 'c_1', amount: 1200.5, month: '2026-06' },
|
|
101
|
+
{ customerName: 'c_2', amount: 800, month: '2026-06' },
|
|
102
102
|
];
|
|
103
103
|
const { data } = await api.setRevenue({ client, throwOnError: true, body: { rows } });
|
|
104
104
|
assert.deepEqual(data, { ok: true, rowsInserted: 2 });
|
|
@@ -107,70 +107,91 @@ test('setRevenue posts the rows in snake_case and parses the booked count', asyn
|
|
|
107
107
|
assert.equal(request.url, '/api/revenue/');
|
|
108
108
|
assert.deepEqual(request.body, {
|
|
109
109
|
rows: [
|
|
110
|
-
{ customer_name: 'c_1', amount:
|
|
111
|
-
{ customer_name: 'c_2', amount:
|
|
110
|
+
{ customer_name: 'c_1', amount: 1200.5, month: '2026-06' },
|
|
111
|
+
{ customer_name: 'c_2', amount: 800, month: '2026-06' },
|
|
112
112
|
],
|
|
113
113
|
});
|
|
114
114
|
});
|
|
115
115
|
});
|
|
116
116
|
|
|
117
|
-
test('
|
|
117
|
+
test('setRevenue posts the revenueId in snake_case and the upsert flag', async () => {
|
|
118
|
+
await withClientReturning({ ok: true, rows_inserted: 1 }, 200, async (client, seenRequests) => {
|
|
119
|
+
const rows = [
|
|
120
|
+
{
|
|
121
|
+
revenueId: 'b3f2b160-6e12-4f1a-9c3d-0a2e7c9b5d41',
|
|
122
|
+
customerName: 'c_1',
|
|
123
|
+
amount: 1300,
|
|
124
|
+
month: '2026-06',
|
|
125
|
+
},
|
|
126
|
+
];
|
|
127
|
+
const { data } = await api.setRevenue({
|
|
128
|
+
client,
|
|
129
|
+
throwOnError: true,
|
|
130
|
+
body: { upsert: true, rows },
|
|
131
|
+
});
|
|
132
|
+
assert.deepEqual(data, { ok: true, rowsInserted: 1 });
|
|
133
|
+
assert.deepEqual(seenRequests[0].body, {
|
|
134
|
+
upsert: true,
|
|
135
|
+
rows: [
|
|
136
|
+
{
|
|
137
|
+
revenue_id: 'b3f2b160-6e12-4f1a-9c3d-0a2e7c9b5d41',
|
|
138
|
+
customer_name: 'c_1',
|
|
139
|
+
amount: 1300,
|
|
140
|
+
month: '2026-06',
|
|
141
|
+
},
|
|
142
|
+
],
|
|
143
|
+
});
|
|
144
|
+
});
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
test('getRevenue parses the returned rows into camelCase', async () => {
|
|
118
148
|
const wireRow = {
|
|
119
149
|
customer_name: 'c_1',
|
|
120
150
|
metric_name: 'revenue',
|
|
121
151
|
month: '2026-06',
|
|
122
|
-
amount:
|
|
152
|
+
amount: 1200.5,
|
|
123
153
|
source: 'api',
|
|
124
154
|
};
|
|
125
155
|
await withClientReturning({ rows: [wireRow] }, 200, async (client) => {
|
|
126
|
-
const { data } = await api.
|
|
156
|
+
const { data } = await api.getRevenue({ client, throwOnError: true });
|
|
127
157
|
assert.deepEqual(data.rows, [
|
|
128
158
|
{
|
|
129
159
|
customerName: 'c_1',
|
|
130
160
|
metricName: 'revenue',
|
|
131
161
|
month: '2026-06',
|
|
132
|
-
amount:
|
|
162
|
+
amount: 1200.5,
|
|
133
163
|
source: 'api',
|
|
134
164
|
},
|
|
135
165
|
]);
|
|
136
166
|
});
|
|
137
167
|
});
|
|
138
168
|
|
|
139
|
-
test('
|
|
169
|
+
test('getRevenue sends the customerNames filter as repeated params', async () => {
|
|
140
170
|
await withClientReturning({ rows: [] }, 200, async (client, seenRequests) => {
|
|
141
|
-
const { data } = await api.
|
|
171
|
+
const { data } = await api.getRevenue({
|
|
142
172
|
client,
|
|
143
173
|
throwOnError: true,
|
|
144
|
-
|
|
174
|
+
query: { customerNames: ['c_1', 'c_2'] },
|
|
145
175
|
});
|
|
146
176
|
assert.deepEqual(data, { rows: [] });
|
|
147
|
-
|
|
177
|
+
const query = new URL(seenRequests[0].url, 'http://x').searchParams;
|
|
178
|
+
assert.deepEqual(query.getAll('customer_names'), ['c_1', 'c_2']);
|
|
148
179
|
});
|
|
149
180
|
});
|
|
150
181
|
|
|
151
|
-
test('
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
const { error, response } = await api.getCustomerRevenue({
|
|
155
|
-
client,
|
|
156
|
-
path: { customerName: 'nobody' },
|
|
157
|
-
});
|
|
158
|
-
assert.equal(response.status, 404);
|
|
159
|
-
assert.deepEqual(error, rejection);
|
|
160
|
-
});
|
|
161
|
-
});
|
|
162
|
-
|
|
163
|
-
test('deleteCustomerRevenue parses the deleted count', async () => {
|
|
164
|
-
await withClientReturning({ ok: true, rows_deleted: 1 }, 200, async (client, seenRequests) => {
|
|
165
|
-
const { data } = await api.deleteCustomerRevenue({
|
|
182
|
+
test('deleteRevenue sends the customerNames filter and parses the deleted count', async () => {
|
|
183
|
+
await withClientReturning({ ok: true, rows_deleted: 2 }, 200, async (client, seenRequests) => {
|
|
184
|
+
const { data } = await api.deleteRevenue({
|
|
166
185
|
client,
|
|
167
186
|
throwOnError: true,
|
|
168
|
-
|
|
187
|
+
query: { customerNames: ['c_1', 'c_2'] },
|
|
169
188
|
});
|
|
170
|
-
assert.deepEqual(data, { ok: true, rowsDeleted:
|
|
189
|
+
assert.deepEqual(data, { ok: true, rowsDeleted: 2 });
|
|
171
190
|
const request = seenRequests[0];
|
|
172
191
|
assert.equal(request.method, 'DELETE');
|
|
173
|
-
|
|
192
|
+
const url = new URL(request.url, 'http://x');
|
|
193
|
+
assert.equal(url.pathname, '/api/revenue/');
|
|
194
|
+
assert.deepEqual(url.searchParams.getAll('customer_names'), ['c_1', 'c_2']);
|
|
174
195
|
});
|
|
175
196
|
});
|
|
176
197
|
|
|
@@ -178,51 +199,50 @@ test('deleteCustomerRevenue parses the deleted count', async () => {
|
|
|
178
199
|
|
|
179
200
|
test('setKeyMetrics posts the rows in snake_case and parses the booked count', async () => {
|
|
180
201
|
await withClientReturning({ ok: true, rows_inserted: 1 }, 200, async (client, seenRequests) => {
|
|
181
|
-
const rows = [
|
|
182
|
-
{ userName: 'u_1', metricName: 'Meetings booked', amount: '3', month: '2026-06' },
|
|
183
|
-
];
|
|
202
|
+
const rows = [{ userName: 'u_1', metricName: 'Meetings booked', amount: 3, month: '2026-06' }];
|
|
184
203
|
const { data } = await api.setKeyMetrics({ client, throwOnError: true, body: { rows } });
|
|
185
204
|
assert.deepEqual(data, { ok: true, rowsInserted: 1 });
|
|
186
205
|
const request = seenRequests[0];
|
|
187
206
|
assert.equal(request.method, 'POST');
|
|
188
207
|
assert.equal(request.url, '/api/key-metrics/');
|
|
189
208
|
assert.deepEqual(request.body, {
|
|
190
|
-
rows: [{ user_name: 'u_1', metric_name: 'Meetings booked', amount:
|
|
209
|
+
rows: [{ user_name: 'u_1', metric_name: 'Meetings booked', amount: 3, month: '2026-06' }],
|
|
191
210
|
});
|
|
192
211
|
});
|
|
193
212
|
});
|
|
194
213
|
|
|
195
|
-
test('
|
|
214
|
+
test('getKeyMetrics parses the returned rows into camelCase', async () => {
|
|
196
215
|
const wireRow = {
|
|
197
216
|
user_name: 'u_1',
|
|
198
217
|
metric_name: 'Meetings booked',
|
|
199
218
|
month: '2026-06',
|
|
200
|
-
amount:
|
|
219
|
+
amount: 3.0,
|
|
201
220
|
source: 'api',
|
|
202
221
|
};
|
|
203
222
|
await withClientReturning({ rows: [wireRow] }, 200, async (client) => {
|
|
204
|
-
const { data } = await api.
|
|
223
|
+
const { data } = await api.getKeyMetrics({ client, throwOnError: true });
|
|
205
224
|
assert.deepEqual(data.rows, [
|
|
206
225
|
{
|
|
207
226
|
userName: 'u_1',
|
|
208
227
|
metricName: 'Meetings booked',
|
|
209
228
|
month: '2026-06',
|
|
210
|
-
amount:
|
|
229
|
+
amount: 3.0,
|
|
211
230
|
source: 'api',
|
|
212
231
|
},
|
|
213
232
|
]);
|
|
214
233
|
});
|
|
215
234
|
});
|
|
216
235
|
|
|
217
|
-
test('
|
|
236
|
+
test('getKeyMetrics sends the userNames filter as repeated params', async () => {
|
|
218
237
|
await withClientReturning({ rows: [] }, 200, async (client, seenRequests) => {
|
|
219
|
-
const { data } = await api.
|
|
238
|
+
const { data } = await api.getKeyMetrics({
|
|
220
239
|
client,
|
|
221
240
|
throwOnError: true,
|
|
222
|
-
|
|
241
|
+
query: { userNames: ['u_1', 'u_2'] },
|
|
223
242
|
});
|
|
224
243
|
assert.deepEqual(data, { rows: [] });
|
|
225
|
-
|
|
244
|
+
const query = new URL(seenRequests[0].url, 'http://x').searchParams;
|
|
245
|
+
assert.deepEqual(query.getAll('user_names'), ['u_1', 'u_2']);
|
|
226
246
|
});
|
|
227
247
|
});
|
|
228
248
|
|
|
@@ -238,17 +258,95 @@ test('setKeyMetrics surfaces a 422 and its details', async () => {
|
|
|
238
258
|
});
|
|
239
259
|
});
|
|
240
260
|
|
|
241
|
-
test('
|
|
242
|
-
await withClientReturning({ ok: true, rows_deleted:
|
|
243
|
-
const { data } = await api.
|
|
261
|
+
test('deleteKeyMetrics sends the userNames filter and parses the deleted count', async () => {
|
|
262
|
+
await withClientReturning({ ok: true, rows_deleted: 2 }, 200, async (client, seenRequests) => {
|
|
263
|
+
const { data } = await api.deleteKeyMetrics({
|
|
244
264
|
client,
|
|
245
265
|
throwOnError: true,
|
|
246
|
-
|
|
266
|
+
query: { userNames: ['u_1', 'u_2'] },
|
|
247
267
|
});
|
|
248
|
-
assert.deepEqual(data, { ok: true, rowsDeleted:
|
|
268
|
+
assert.deepEqual(data, { ok: true, rowsDeleted: 2 });
|
|
249
269
|
const request = seenRequests[0];
|
|
250
270
|
assert.equal(request.method, 'DELETE');
|
|
251
|
-
|
|
271
|
+
const url = new URL(request.url, 'http://x');
|
|
272
|
+
assert.equal(url.pathname, '/api/key-metrics/');
|
|
273
|
+
assert.deepEqual(url.searchParams.getAll('user_names'), ['u_1', 'u_2']);
|
|
274
|
+
});
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
// ── Margin ────────────────────────────────────────────────────────────────────
|
|
278
|
+
|
|
279
|
+
test('getMargin parses the returned rows into camelCase', async () => {
|
|
280
|
+
const wireRow = {
|
|
281
|
+
customer_name: 'c_1',
|
|
282
|
+
revenue: 1000.0,
|
|
283
|
+
cost: 200.0,
|
|
284
|
+
margin: 800.0,
|
|
285
|
+
margin_pct: 80.0,
|
|
286
|
+
profitability_status: 'healthy',
|
|
287
|
+
};
|
|
288
|
+
await withClientReturning(
|
|
289
|
+
{ rows: [wireRow], next_cursor: null, has_more: false },
|
|
290
|
+
200,
|
|
291
|
+
async (client) => {
|
|
292
|
+
const { data } = await api.getMargin({ client, throwOnError: true });
|
|
293
|
+
assert.deepEqual(data.rows, [
|
|
294
|
+
{
|
|
295
|
+
customerName: 'c_1',
|
|
296
|
+
revenue: 1000.0,
|
|
297
|
+
cost: 200.0,
|
|
298
|
+
margin: 800.0,
|
|
299
|
+
marginPct: 80.0,
|
|
300
|
+
profitabilityStatus: 'healthy',
|
|
301
|
+
},
|
|
302
|
+
]);
|
|
303
|
+
}
|
|
304
|
+
);
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
test('getMargin sends the cursor and limit query params', async () => {
|
|
308
|
+
await withClientReturning(
|
|
309
|
+
{ rows: [], next_cursor: 'Y19h', has_more: true },
|
|
310
|
+
200,
|
|
311
|
+
async (client, seenRequests) => {
|
|
312
|
+
const { data } = await api.getMargin({
|
|
313
|
+
client,
|
|
314
|
+
throwOnError: true,
|
|
315
|
+
query: { cursor: 'abc123', limit: 2 },
|
|
316
|
+
});
|
|
317
|
+
assert.equal(data.nextCursor, 'Y19h');
|
|
318
|
+
assert.equal(data.hasMore, true);
|
|
319
|
+
const query = new URL(seenRequests[0].url, 'http://x').searchParams;
|
|
320
|
+
assert.equal(query.get('cursor'), 'abc123');
|
|
321
|
+
assert.equal(query.get('limit'), '2');
|
|
322
|
+
}
|
|
323
|
+
);
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
test('getMargin sends the customerNames filter as repeated params', async () => {
|
|
327
|
+
await withClientReturning({ rows: [] }, 200, async (client, seenRequests) => {
|
|
328
|
+
const { data } = await api.getMargin({
|
|
329
|
+
client,
|
|
330
|
+
throwOnError: true,
|
|
331
|
+
query: { customerNames: ['c_1', 'c_2'] },
|
|
332
|
+
});
|
|
333
|
+
assert.deepEqual(data, { rows: [] });
|
|
334
|
+
const query = new URL(seenRequests[0].url, 'http://x').searchParams;
|
|
335
|
+
assert.deepEqual(query.getAll('customer_names'), ['c_1', 'c_2']);
|
|
336
|
+
});
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
test('getMargin sends the dateFrom/dateTo/applyCredits filters in snake_case', async () => {
|
|
340
|
+
await withClientReturning({ rows: [] }, 200, async (client, seenRequests) => {
|
|
341
|
+
await api.getMargin({
|
|
342
|
+
client,
|
|
343
|
+
throwOnError: true,
|
|
344
|
+
query: { dateFrom: '2026-06-01', dateTo: '2026-06-30', applyCredits: true },
|
|
345
|
+
});
|
|
346
|
+
const query = new URL(seenRequests[0].url, 'http://x').searchParams;
|
|
347
|
+
assert.equal(query.get('date_from'), '2026-06-01');
|
|
348
|
+
assert.equal(query.get('date_to'), '2026-06-30');
|
|
349
|
+
assert.equal(query.get('apply_credits'), 'true');
|
|
252
350
|
});
|
|
253
351
|
});
|
|
254
352
|
|
|
@@ -290,41 +388,50 @@ test('setCustomersTags leaves tag keys untouched, even when they contain undersc
|
|
|
290
388
|
);
|
|
291
389
|
});
|
|
292
390
|
|
|
293
|
-
test('
|
|
391
|
+
test('getCustomersTags parses the returned customers into camelCase', async () => {
|
|
294
392
|
const wireCustomer = { customer_name: 'c_1', tags: { plan: 'pro' } };
|
|
295
393
|
await withClientReturning(
|
|
296
394
|
{ customers: [wireCustomer], next_cursor: null, has_more: false },
|
|
297
395
|
200,
|
|
298
396
|
async (client) => {
|
|
299
|
-
const { data } = await api.
|
|
397
|
+
const { data } = await api.getCustomersTags({ client, throwOnError: true });
|
|
300
398
|
assert.deepEqual(data.customers, [{ customerName: 'c_1', tags: { plan: 'pro' } }]);
|
|
301
399
|
}
|
|
302
400
|
);
|
|
303
401
|
});
|
|
304
402
|
|
|
305
|
-
test('
|
|
306
|
-
const
|
|
307
|
-
await withClientReturning(
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
403
|
+
test('getCustomersTags sends the customerNames filter, leaving tag keys untouched', async () => {
|
|
404
|
+
const wireCustomer = { customer_name: 'c_1', tags: { plan: 'pro', utm_source: 'ads' } };
|
|
405
|
+
await withClientReturning(
|
|
406
|
+
{ customers: [wireCustomer], next_cursor: null, has_more: false },
|
|
407
|
+
200,
|
|
408
|
+
async (client, seenRequests) => {
|
|
409
|
+
const { data } = await api.getCustomersTags({
|
|
410
|
+
client,
|
|
411
|
+
throwOnError: true,
|
|
412
|
+
query: { customerNames: ['c_1', 'c_2'] },
|
|
413
|
+
});
|
|
414
|
+
assert.deepEqual(data.customers, [
|
|
415
|
+
{ customerName: 'c_1', tags: { plan: 'pro', utm_source: 'ads' } },
|
|
416
|
+
]);
|
|
417
|
+
const query = new URL(seenRequests[0].url, 'http://x').searchParams;
|
|
418
|
+
assert.deepEqual(query.getAll('customer_names'), ['c_1', 'c_2']);
|
|
419
|
+
}
|
|
420
|
+
);
|
|
316
421
|
});
|
|
317
422
|
|
|
318
|
-
test('
|
|
319
|
-
await withClientReturning({ ok: true, tags_deleted:
|
|
320
|
-
const { data } = await api.
|
|
423
|
+
test('deleteCustomersTags sends the customerNames filter and parses the deleted count', async () => {
|
|
424
|
+
await withClientReturning({ ok: true, tags_deleted: 3 }, 200, async (client, seenRequests) => {
|
|
425
|
+
const { data } = await api.deleteCustomersTags({
|
|
321
426
|
client,
|
|
322
427
|
throwOnError: true,
|
|
323
|
-
|
|
428
|
+
query: { customerNames: ['c_1', 'c_2'] },
|
|
324
429
|
});
|
|
325
|
-
assert.deepEqual(data, { ok: true, tagsDeleted:
|
|
430
|
+
assert.deepEqual(data, { ok: true, tagsDeleted: 3 });
|
|
326
431
|
const request = seenRequests[0];
|
|
327
432
|
assert.equal(request.method, 'DELETE');
|
|
328
|
-
|
|
433
|
+
const url = new URL(request.url, 'http://x');
|
|
434
|
+
assert.equal(url.pathname, '/api/customer-tags/');
|
|
435
|
+
assert.deepEqual(url.searchParams.getAll('customer_names'), ['c_1', 'c_2']);
|
|
329
436
|
});
|
|
330
437
|
});
|