@object-ui/data-objectstack 17.5.0 → 17.7.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/CHANGELOG.md +2311 -0
- package/README.md +557 -11
- package/dist/index.cjs +1663 -463
- package/dist/index.d.cts +1780 -518
- package/dist/index.d.ts +1780 -518
- package/dist/index.js +1648 -457
- package/package.json +9 -9
- package/dist/tsconfig.tsbuildinfo +0 -1
- package/src/aggregate-capability.test.ts +0 -177
- package/src/apiAccessDenied.test.ts +0 -233
- package/src/appAccessProbe.test.ts +0 -147
- package/src/bulkDelete.test.ts +0 -75
- package/src/bulkUpdate.test.ts +0 -93
- package/src/cache/MetadataCache.test.ts +0 -477
- package/src/cache/MetadataCache.ts +0 -289
- package/src/cloud-surface-retired-4152.pin.test.ts +0 -171
- package/src/connection.test.ts +0 -188
- package/src/contracts.ts +0 -115
- package/src/deleteView.homes.test.ts +0 -389
- package/src/deleteViewContract.types.test.ts +0 -246
- package/src/errors.test.ts +0 -426
- package/src/errors.ts +0 -293
- package/src/exportDownload.test.ts +0 -141
- package/src/filter-entry-translation.test.ts +0 -360
- package/src/filter-operator-ast-parity.test.ts +0 -135
- package/src/getObjectSchema.test.ts +0 -145
- package/src/httpFailureLogging.test.ts +0 -109
- package/src/importJob.test.ts +0 -71
- package/src/index.ts +0 -4442
- package/src/integration.ts +0 -200
- package/src/listViewOverrides.test.ts +0 -241
- package/src/listViews.test.ts +0 -175
- package/src/metadata-client.get-envelope.test.ts +0 -171
- package/src/metadata-client.saveAdvisories.test.ts +0 -226
- package/src/metadata-client.test.ts +0 -303
- package/src/metadata-client.ts +0 -1046
- package/src/occ.test.ts +0 -101
- package/src/onMutation.test.ts +0 -589
- package/src/onSaveAdvisory.test.ts +0 -311
- package/src/onWriteWarning.test.ts +0 -273
- package/src/orderby-serialization.test.ts +0 -115
- package/src/queryDataset.test.ts +0 -543
- package/src/searchAll.test.ts +0 -120
- package/src/security.ts +0 -243
- package/src/spec-symbol-batch6.test.ts +0 -326
- package/src/studio.ts +0 -152
- package/src/updateView.draft.test.ts +0 -242
- package/src/userState.test.ts +0 -315
- package/src/userState.ts +0 -238
- package/src/v3-compat.test.ts +0 -229
- package/src/validation-error.test.ts +0 -94
- package/src/viewCacheInvalidation.pin.test.ts +0 -260
- package/src/viewInvalidationSeam.pin.test.ts +0 -162
|
@@ -1,109 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* objectui#4042 half 2 — a request failure must SAY which request failed.
|
|
3
|
-
*
|
|
4
|
-
* `@objectstack/client` reports every non-2xx as
|
|
5
|
-
* `logger.error("HTTP request failed", undefined, { method, url, status, error })`.
|
|
6
|
-
* The console's logger used to forward that verbatim, so the identifying
|
|
7
|
-
* fields lived only in the third argument: anything that flattens a console
|
|
8
|
-
* record to text rendered them `[object Object]` / `Object`, and a screenful of
|
|
9
|
-
* failures told you nothing about which URL or which status. These tests pin
|
|
10
|
-
* that the message string itself now carries method + url + status (+ code).
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
import { describe, it, expect, vi, afterEach } from 'vitest';
|
|
14
|
-
import { formatHttpFailureMessage, createQuietHttpLogger } from './index';
|
|
15
|
-
|
|
16
|
-
const META_401 = {
|
|
17
|
-
method: 'GET',
|
|
18
|
-
url: '/api/v1/meta/object',
|
|
19
|
-
status: 401,
|
|
20
|
-
error: { code: 'UNAUTHORIZED', message: 'Unauthorized' },
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
describe('formatHttpFailureMessage', () => {
|
|
24
|
-
it('folds method, url and status into the message', () => {
|
|
25
|
-
expect(formatHttpFailureMessage('HTTP request failed', META_401)).toBe(
|
|
26
|
-
'HTTP request failed: GET /api/v1/meta/object -> 401 [UNAUTHORIZED]',
|
|
27
|
-
);
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
it('omits the code when the body carries none', () => {
|
|
31
|
-
expect(
|
|
32
|
-
formatHttpFailureMessage('HTTP request failed', {
|
|
33
|
-
method: 'POST',
|
|
34
|
-
url: '/api/v1/data/task',
|
|
35
|
-
status: 500,
|
|
36
|
-
}),
|
|
37
|
-
).toBe('HTTP request failed: POST /api/v1/data/task -> 500');
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
it('reads the `statusCode` spelling too', () => {
|
|
41
|
-
expect(
|
|
42
|
-
formatHttpFailureMessage('HTTP request failed', { url: '/api/v1/meta/app', statusCode: 403 }),
|
|
43
|
-
).toBe('HTTP request failed: GET /api/v1/meta/app -> 403');
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
it('returns null — not a husk — when meta identifies nothing', () => {
|
|
47
|
-
expect(formatHttpFailureMessage('HTTP request failed', undefined)).toBeNull();
|
|
48
|
-
expect(formatHttpFailureMessage('HTTP request failed', {})).toBeNull();
|
|
49
|
-
expect(formatHttpFailureMessage('HTTP request failed', { error: { message: 'boom' } })).toBeNull();
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
it('never yields the un-diagnosable string the card reported', () => {
|
|
53
|
-
const line = formatHttpFailureMessage('HTTP request failed', META_401)!;
|
|
54
|
-
expect(line).not.toContain('[object Object]');
|
|
55
|
-
expect(line).toContain('/api/v1/meta/object');
|
|
56
|
-
expect(line).toContain('401');
|
|
57
|
-
});
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
describe('createQuietHttpLogger', () => {
|
|
61
|
-
afterEach(() => vi.restoreAllMocks());
|
|
62
|
-
|
|
63
|
-
it('logs a 401 as an error whose FIRST argument identifies the request', () => {
|
|
64
|
-
const spy = vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
65
|
-
createQuietHttpLogger().error('HTTP request failed', undefined, META_401);
|
|
66
|
-
|
|
67
|
-
expect(spy).toHaveBeenCalledTimes(1);
|
|
68
|
-
const [message, , meta] = spy.mock.calls[0];
|
|
69
|
-
expect(message).toBe('HTTP request failed: GET /api/v1/meta/object -> 401 [UNAUTHORIZED]');
|
|
70
|
-
// The structured bag is still handed over for DevTools to expand — the
|
|
71
|
-
// string is added alongside it, not instead of it.
|
|
72
|
-
expect(meta).toEqual(META_401);
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
it('keeps demoting an expected 404, and identifies it in the demoted line too', () => {
|
|
76
|
-
const errorSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
77
|
-
const debugSpy = vi.spyOn(console, 'debug').mockImplementation(() => {});
|
|
78
|
-
|
|
79
|
-
createQuietHttpLogger().error('HTTP request failed', undefined, {
|
|
80
|
-
method: 'GET',
|
|
81
|
-
url: '/api/v1/data/sys_presence',
|
|
82
|
-
status: 404,
|
|
83
|
-
error: { code: 'OBJECT_NOT_FOUND' },
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
expect(errorSpy).not.toHaveBeenCalled();
|
|
87
|
-
expect(debugSpy).toHaveBeenCalledTimes(1);
|
|
88
|
-
expect(debugSpy.mock.calls[0][0]).toContain('/api/v1/data/sys_presence -> 404');
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
it('does NOT silence a 401 — only 404-on-an-optional-collection is demoted', () => {
|
|
92
|
-
const errorSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
93
|
-
const debugSpy = vi.spyOn(console, 'debug').mockImplementation(() => {});
|
|
94
|
-
|
|
95
|
-
// Session expiry mid-use: the console gates doomed requests, it does not
|
|
96
|
-
// hide the ones that still fail.
|
|
97
|
-
createQuietHttpLogger().error('HTTP request failed', undefined, META_401);
|
|
98
|
-
|
|
99
|
-
expect(errorSpy).toHaveBeenCalledTimes(1);
|
|
100
|
-
expect(debugSpy).not.toHaveBeenCalled();
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
it('falls back to the bare message when there is nothing to identify', () => {
|
|
104
|
-
const spy = vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
105
|
-
createQuietHttpLogger().error('Something broke', undefined, undefined);
|
|
106
|
-
|
|
107
|
-
expect(spy.mock.calls[0][0]).toBe('Something broke');
|
|
108
|
-
});
|
|
109
|
-
});
|
package/src/importJob.test.ts
DELETED
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ObjectUI
|
|
3
|
-
* Copyright (c) 2024-present ObjectStack Inc.
|
|
4
|
-
*
|
|
5
|
-
* This source code is licensed under the MIT license found in the
|
|
6
|
-
* LICENSE file in the root directory of this source tree.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import { describe, it, expect, vi } from 'vitest';
|
|
10
|
-
import { ObjectStackAdapter } from './index';
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Adapter over a mock client `data` namespace. The async import-job methods are
|
|
14
|
-
* thin pass-throughs to the client SDK, so we assert delegation + argument
|
|
15
|
-
* shaping, plus graceful degradation when the client lacks the job API.
|
|
16
|
-
*/
|
|
17
|
-
function makeDS(stub: Record<string, any>) {
|
|
18
|
-
const ds: any = new ObjectStackAdapter({
|
|
19
|
-
baseUrl: 'http://test.local',
|
|
20
|
-
fetch: vi.fn(async () => new Response(JSON.stringify({ success: true, data: { capabilities: {}, routes: {} } }), {
|
|
21
|
-
status: 200,
|
|
22
|
-
headers: { 'Content-Type': 'application/json' },
|
|
23
|
-
})),
|
|
24
|
-
});
|
|
25
|
-
ds.connected = true;
|
|
26
|
-
ds.connectionState = 'connected';
|
|
27
|
-
ds.client = { data: stub };
|
|
28
|
-
return ds;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
describe('ObjectStackAdapter async import jobs', () => {
|
|
32
|
-
it('createImportJob delegates to client.data.createImportJob', async () => {
|
|
33
|
-
const createImportJob = vi.fn().mockResolvedValue({ jobId: 'imp_1', object: 'task', status: 'pending', total: 2 });
|
|
34
|
-
const ds = makeDS({ createImportJob });
|
|
35
|
-
const req = { format: 'json' as const, rows: [{ a: 1 }, { a: 2 }], writeMode: 'upsert' as const, matchFields: ['a'] };
|
|
36
|
-
const res = await ds.createImportJob('task', req);
|
|
37
|
-
expect(createImportJob).toHaveBeenCalledTimes(1);
|
|
38
|
-
expect(createImportJob.mock.calls[0]).toEqual(['task', req]);
|
|
39
|
-
expect(res).toMatchObject({ jobId: 'imp_1', status: 'pending', total: 2 });
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
it('getImportJobProgress / getImportJobResults / cancelImportJob delegate by jobId', async () => {
|
|
43
|
-
const getImportJobProgress = vi.fn().mockResolvedValue({ jobId: 'imp_1', status: 'running', percentComplete: 50 });
|
|
44
|
-
const getImportJobResults = vi.fn().mockResolvedValue({ jobId: 'imp_1', status: 'succeeded', results: [], resultsTruncated: false });
|
|
45
|
-
const cancelImportJob = vi.fn().mockResolvedValue({ success: true });
|
|
46
|
-
const ds = makeDS({ createImportJob: vi.fn(), getImportJobProgress, getImportJobResults, cancelImportJob });
|
|
47
|
-
|
|
48
|
-
expect((await ds.getImportJobProgress('imp_1')).percentComplete).toBe(50);
|
|
49
|
-
expect(getImportJobProgress).toHaveBeenCalledWith('imp_1');
|
|
50
|
-
|
|
51
|
-
expect((await ds.getImportJobResults('imp_1')).resultsTruncated).toBe(false);
|
|
52
|
-
expect(getImportJobResults).toHaveBeenCalledWith('imp_1');
|
|
53
|
-
|
|
54
|
-
await ds.cancelImportJob('imp_1');
|
|
55
|
-
expect(cancelImportJob).toHaveBeenCalledWith('imp_1');
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
it('listImportJobs forwards filters and returns the jobs array', async () => {
|
|
59
|
-
const listImportJobs = vi.fn().mockResolvedValue([{ jobId: 'imp_1', object: 'task', status: 'succeeded' }]);
|
|
60
|
-
const ds = makeDS({ createImportJob: vi.fn(), listImportJobs });
|
|
61
|
-
const jobs = await ds.listImportJobs({ object: 'task', status: 'succeeded', limit: 10 });
|
|
62
|
-
expect(listImportJobs).toHaveBeenCalledWith({ object: 'task', status: 'succeeded', limit: 10 });
|
|
63
|
-
expect(jobs).toHaveLength(1);
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
it('throws UNSUPPORTED_OPERATION when the client lacks the job API', async () => {
|
|
67
|
-
const ds = makeDS({ import: vi.fn() }); // sync import only, no createImportJob
|
|
68
|
-
await expect(ds.createImportJob('task', { format: 'json', rows: [] })).rejects.toMatchObject({ code: 'UNSUPPORTED_OPERATION' });
|
|
69
|
-
await expect(ds.getImportJobProgress('imp_1')).rejects.toMatchObject({ code: 'UNSUPPORTED_OPERATION' });
|
|
70
|
-
});
|
|
71
|
-
});
|