@wix/bex-core 2.328.0 → 2.329.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/cjs/assets/locale/messages_ar.json +12 -0
- package/dist/cjs/assets/locale/messages_bg.json +12 -0
- package/dist/cjs/assets/locale/messages_ca.json +12 -0
- package/dist/cjs/assets/locale/messages_cs.json +12 -0
- package/dist/cjs/assets/locale/messages_da.json +17 -5
- package/dist/cjs/assets/locale/messages_de.json +12 -0
- package/dist/cjs/assets/locale/messages_el.json +12 -0
- package/dist/cjs/assets/locale/messages_en.json +12 -0
- package/dist/cjs/assets/locale/messages_es.json +16 -4
- package/dist/cjs/assets/locale/messages_fi.json +12 -0
- package/dist/cjs/assets/locale/messages_fr.json +12 -0
- package/dist/cjs/assets/locale/messages_he.json +12 -0
- package/dist/cjs/assets/locale/messages_hi.json +17 -5
- package/dist/cjs/assets/locale/messages_hu.json +12 -0
- package/dist/cjs/assets/locale/messages_id.json +17 -5
- package/dist/cjs/assets/locale/messages_it.json +17 -5
- package/dist/cjs/assets/locale/messages_ja.json +12 -0
- package/dist/cjs/assets/locale/messages_ko.json +17 -5
- package/dist/cjs/assets/locale/messages_lt.json +12 -0
- package/dist/cjs/assets/locale/messages_ms.json +12 -0
- package/dist/cjs/assets/locale/messages_nl.json +16 -4
- package/dist/cjs/assets/locale/messages_no.json +12 -0
- package/dist/cjs/assets/locale/messages_pl.json +12 -0
- package/dist/cjs/assets/locale/messages_pt.json +16 -4
- package/dist/cjs/assets/locale/messages_ro.json +12 -0
- package/dist/cjs/assets/locale/messages_ru.json +12 -0
- package/dist/cjs/assets/locale/messages_sk.json +12 -0
- package/dist/cjs/assets/locale/messages_sl.json +12 -0
- package/dist/cjs/assets/locale/messages_sv.json +17 -5
- package/dist/cjs/assets/locale/messages_th.json +17 -5
- package/dist/cjs/assets/locale/messages_tl.json +12 -0
- package/dist/cjs/assets/locale/messages_tr.json +17 -5
- package/dist/cjs/assets/locale/messages_uk.json +12 -0
- package/dist/cjs/assets/locale/messages_vi.json +17 -5
- package/dist/cjs/assets/locale/messages_zh.json +12 -0
- package/dist/cjs/assets/locale/version_file +1 -1
- package/dist/cjs/assets/visual-e2e-locale/messages_visuale2e.json +4 -1
- package/dist/cjs/hooks/useImport.js +7 -5
- package/dist/cjs/hooks/useImport.js.map +1 -1
- package/dist/cjs/hooks/useImportConfig.js.map +1 -1
- package/dist/cjs/services/translations.js.map +1 -1
- package/dist/cjs/state/ImportState/ImportState.js +363 -109
- package/dist/cjs/state/ImportState/ImportState.js.map +1 -1
- package/dist/cjs/state/ImportState/__tests__/ImportState.engine.spec.js +536 -0
- package/dist/cjs/state/ImportState/__tests__/ImportState.engine.spec.js.map +1 -0
- package/dist/cjs/state/ImportState/__tests__/hubJobClient.spec.js +209 -0
- package/dist/cjs/state/ImportState/__tests__/hubJobClient.spec.js.map +1 -0
- package/dist/cjs/state/ImportState/__tests__/importJobPoller.spec.js +86 -0
- package/dist/cjs/state/ImportState/__tests__/importJobPoller.spec.js.map +1 -0
- package/dist/cjs/state/ImportState/__tests__/parseCsv.spec.js +52 -0
- package/dist/cjs/state/ImportState/__tests__/parseCsv.spec.js.map +1 -0
- package/dist/cjs/state/ImportState/__tests__/rewriteCsv.spec.js +81 -0
- package/dist/cjs/state/ImportState/__tests__/rewriteCsv.spec.js.map +1 -0
- package/dist/cjs/state/ImportState/fieldEdit.js +46 -0
- package/dist/cjs/state/ImportState/fieldEdit.js.map +1 -0
- package/dist/cjs/state/ImportState/hubJobClient.js +101 -0
- package/dist/cjs/state/ImportState/hubJobClient.js.map +1 -0
- package/dist/cjs/state/ImportState/importJobPoller.js +44 -0
- package/dist/cjs/state/ImportState/importJobPoller.js.map +1 -0
- package/dist/cjs/state/ImportState/index.js +12 -0
- package/dist/cjs/state/ImportState/index.js.map +1 -1
- package/dist/cjs/state/ImportState/parseCsv.js +39 -10
- package/dist/cjs/state/ImportState/parseCsv.js.map +1 -1
- package/dist/cjs/state/ImportState/rewriteCsv.js +46 -0
- package/dist/cjs/state/ImportState/rewriteCsv.js.map +1 -0
- package/dist/cjs/testkit/WixPatternsContainer.driver.js +4 -1
- package/dist/cjs/testkit/WixPatternsContainer.driver.js.map +1 -1
- package/dist/cjs/testkit/client.js +6 -0
- package/dist/cjs/testkit/client.js.map +1 -1
- package/dist/cjs/testkit/hubJobImportMocks.js +166 -0
- package/dist/cjs/testkit/hubJobImportMocks.js.map +1 -0
- package/dist/cjs/testkit/index.js +6 -0
- package/dist/cjs/testkit/index.js.map +1 -1
- package/dist/esm/assets/locale/messages_ar.json +12 -0
- package/dist/esm/assets/locale/messages_bg.json +12 -0
- package/dist/esm/assets/locale/messages_ca.json +12 -0
- package/dist/esm/assets/locale/messages_cs.json +12 -0
- package/dist/esm/assets/locale/messages_da.json +17 -5
- package/dist/esm/assets/locale/messages_de.json +12 -0
- package/dist/esm/assets/locale/messages_el.json +12 -0
- package/dist/esm/assets/locale/messages_en.json +12 -0
- package/dist/esm/assets/locale/messages_es.json +16 -4
- package/dist/esm/assets/locale/messages_fi.json +12 -0
- package/dist/esm/assets/locale/messages_fr.json +12 -0
- package/dist/esm/assets/locale/messages_he.json +12 -0
- package/dist/esm/assets/locale/messages_hi.json +17 -5
- package/dist/esm/assets/locale/messages_hu.json +12 -0
- package/dist/esm/assets/locale/messages_id.json +17 -5
- package/dist/esm/assets/locale/messages_it.json +17 -5
- package/dist/esm/assets/locale/messages_ja.json +12 -0
- package/dist/esm/assets/locale/messages_ko.json +17 -5
- package/dist/esm/assets/locale/messages_lt.json +12 -0
- package/dist/esm/assets/locale/messages_ms.json +12 -0
- package/dist/esm/assets/locale/messages_nl.json +16 -4
- package/dist/esm/assets/locale/messages_no.json +12 -0
- package/dist/esm/assets/locale/messages_pl.json +12 -0
- package/dist/esm/assets/locale/messages_pt.json +16 -4
- package/dist/esm/assets/locale/messages_ro.json +12 -0
- package/dist/esm/assets/locale/messages_ru.json +12 -0
- package/dist/esm/assets/locale/messages_sk.json +12 -0
- package/dist/esm/assets/locale/messages_sl.json +12 -0
- package/dist/esm/assets/locale/messages_sv.json +17 -5
- package/dist/esm/assets/locale/messages_th.json +17 -5
- package/dist/esm/assets/locale/messages_tl.json +12 -0
- package/dist/esm/assets/locale/messages_tr.json +17 -5
- package/dist/esm/assets/locale/messages_uk.json +12 -0
- package/dist/esm/assets/locale/messages_vi.json +17 -5
- package/dist/esm/assets/locale/messages_zh.json +12 -0
- package/dist/esm/assets/locale/version_file +1 -1
- package/dist/esm/assets/visual-e2e-locale/messages_visuale2e.json +4 -1
- package/dist/esm/hooks/useImport.js +5 -4
- package/dist/esm/hooks/useImport.js.map +1 -1
- package/dist/esm/hooks/useImportConfig.js.map +1 -1
- package/dist/esm/state/ImportState/ImportState.js +293 -101
- package/dist/esm/state/ImportState/ImportState.js.map +1 -1
- package/dist/esm/state/ImportState/__tests__/ImportState.engine.spec.js +385 -0
- package/dist/esm/state/ImportState/__tests__/ImportState.engine.spec.js.map +1 -0
- package/dist/esm/state/ImportState/__tests__/hubJobClient.spec.js +139 -0
- package/dist/esm/state/ImportState/__tests__/hubJobClient.spec.js.map +1 -0
- package/dist/esm/state/ImportState/__tests__/importJobPoller.spec.js +62 -0
- package/dist/esm/state/ImportState/__tests__/importJobPoller.spec.js.map +1 -0
- package/dist/esm/state/ImportState/__tests__/parseCsv.spec.js +59 -0
- package/dist/esm/state/ImportState/__tests__/parseCsv.spec.js.map +1 -0
- package/dist/esm/state/ImportState/__tests__/rewriteCsv.spec.js +68 -0
- package/dist/esm/state/ImportState/__tests__/rewriteCsv.spec.js.map +1 -0
- package/dist/esm/state/ImportState/fieldEdit.js +33 -0
- package/dist/esm/state/ImportState/fieldEdit.js.map +1 -0
- package/dist/esm/state/ImportState/hubJobClient.js +43 -0
- package/dist/esm/state/ImportState/hubJobClient.js.map +1 -0
- package/dist/esm/state/ImportState/importJobPoller.js +34 -0
- package/dist/esm/state/ImportState/importJobPoller.js.map +1 -0
- package/dist/esm/state/ImportState/index.js +2 -0
- package/dist/esm/state/ImportState/index.js.map +1 -1
- package/dist/esm/state/ImportState/parseCsv.js +34 -10
- package/dist/esm/state/ImportState/parseCsv.js.map +1 -1
- package/dist/esm/state/ImportState/rewriteCsv.js +39 -0
- package/dist/esm/state/ImportState/rewriteCsv.js.map +1 -0
- package/dist/esm/testkit/WixPatternsContainer.driver.js +4 -1
- package/dist/esm/testkit/WixPatternsContainer.driver.js.map +1 -1
- package/dist/esm/testkit/client.js +1 -0
- package/dist/esm/testkit/client.js.map +1 -1
- package/dist/esm/testkit/hubJobImportMocks.js +173 -0
- package/dist/esm/testkit/hubJobImportMocks.js.map +1 -0
- package/dist/esm/testkit/index.js +1 -0
- package/dist/esm/testkit/index.js.map +1 -1
- package/dist/types/hooks/useImport.d.ts +5 -3
- package/dist/types/hooks/useImport.d.ts.map +1 -1
- package/dist/types/hooks/useImportConfig.d.ts +2 -3
- package/dist/types/hooks/useImportConfig.d.ts.map +1 -1
- package/dist/types/services/translations.d.ts +1 -1
- package/dist/types/services/translations.d.ts.map +1 -1
- package/dist/types/state/ImportState/ImportState.d.ts +118 -20
- package/dist/types/state/ImportState/ImportState.d.ts.map +1 -1
- package/dist/types/state/ImportState/__tests__/ImportState.engine.spec.d.ts +2 -0
- package/dist/types/state/ImportState/__tests__/ImportState.engine.spec.d.ts.map +1 -0
- package/dist/types/state/ImportState/__tests__/hubJobClient.spec.d.ts +2 -0
- package/dist/types/state/ImportState/__tests__/hubJobClient.spec.d.ts.map +1 -0
- package/dist/types/state/ImportState/__tests__/importJobPoller.spec.d.ts +2 -0
- package/dist/types/state/ImportState/__tests__/importJobPoller.spec.d.ts.map +1 -0
- package/dist/types/state/ImportState/__tests__/parseCsv.spec.d.ts +2 -0
- package/dist/types/state/ImportState/__tests__/parseCsv.spec.d.ts.map +1 -0
- package/dist/types/state/ImportState/__tests__/rewriteCsv.spec.d.ts +2 -0
- package/dist/types/state/ImportState/__tests__/rewriteCsv.spec.d.ts.map +1 -0
- package/dist/types/state/ImportState/fieldEdit.d.ts +10 -0
- package/dist/types/state/ImportState/fieldEdit.d.ts.map +1 -0
- package/dist/types/state/ImportState/hubJobClient.d.ts +47 -0
- package/dist/types/state/ImportState/hubJobClient.d.ts.map +1 -0
- package/dist/types/state/ImportState/importJobPoller.d.ts +20 -0
- package/dist/types/state/ImportState/importJobPoller.d.ts.map +1 -0
- package/dist/types/state/ImportState/index.d.ts +2 -0
- package/dist/types/state/ImportState/index.d.ts.map +1 -1
- package/dist/types/state/ImportState/parseCsv.d.ts +7 -0
- package/dist/types/state/ImportState/parseCsv.d.ts.map +1 -1
- package/dist/types/state/ImportState/rewriteCsv.d.ts +25 -0
- package/dist/types/state/ImportState/rewriteCsv.d.ts.map +1 -0
- package/dist/types/testkit/WixPatternsContainer.driver.d.ts +22 -0
- package/dist/types/testkit/WixPatternsContainer.driver.d.ts.map +1 -1
- package/dist/types/testkit/client.d.ts +1 -0
- package/dist/types/testkit/client.d.ts.map +1 -1
- package/dist/types/testkit/hubJobImportMocks.d.ts +40 -0
- package/dist/types/testkit/hubJobImportMocks.d.ts.map +1 -0
- package/dist/types/testkit/index.d.ts +1 -0
- package/dist/types/testkit/index.d.ts.map +1 -1
- package/package.json +4 -4
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _hubJobClient = require("../hubJobClient");
|
|
4
|
+
var _types = require("@wix/bex-utils/@wix/ambassador-hub-v1-job/types");
|
|
5
|
+
// Mock the SDK http factories so each returns an identifiable descriptor; the
|
|
6
|
+
// mocked httpClient then routes on `__fn` and we can assert the payloads.
|
|
7
|
+
jest.mock('@wix/bex-utils/@wix/ambassador-hub-v1-job/http', () => ({
|
|
8
|
+
createFileUploadUrl: jest.fn(payload => ({
|
|
9
|
+
__fn: 'createFileUploadUrl',
|
|
10
|
+
payload
|
|
11
|
+
})),
|
|
12
|
+
submitJob: jest.fn(payload => ({
|
|
13
|
+
__fn: 'submitJob',
|
|
14
|
+
payload
|
|
15
|
+
})),
|
|
16
|
+
getJob: jest.fn(payload => ({
|
|
17
|
+
__fn: 'getJob',
|
|
18
|
+
payload
|
|
19
|
+
})),
|
|
20
|
+
terminateJob: jest.fn(payload => ({
|
|
21
|
+
__fn: 'terminateJob',
|
|
22
|
+
payload
|
|
23
|
+
})),
|
|
24
|
+
listMovementLogs: jest.fn(payload => ({
|
|
25
|
+
__fn: 'listMovementLogs',
|
|
26
|
+
payload
|
|
27
|
+
}))
|
|
28
|
+
}));
|
|
29
|
+
const RESPONSES = {
|
|
30
|
+
createFileUploadUrl: {
|
|
31
|
+
uploadUrl: 'https://upload.example/abc',
|
|
32
|
+
fileId: 'file-1'
|
|
33
|
+
},
|
|
34
|
+
submitJob: {
|
|
35
|
+
job: {
|
|
36
|
+
id: 'job-1'
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
getJob: {
|
|
40
|
+
job: {
|
|
41
|
+
id: 'job-1',
|
|
42
|
+
status: 'IN_PROGRESS'
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
terminateJob: {},
|
|
46
|
+
listMovementLogs: {
|
|
47
|
+
logs: [{
|
|
48
|
+
sourceItemId: '3'
|
|
49
|
+
}]
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
function makeDeps() {
|
|
53
|
+
// Routes RPC factories on `__fn`; the file upload comes in as a raw request
|
|
54
|
+
// config (no `__fn`) and just needs to resolve.
|
|
55
|
+
const request = jest.fn(async req => ({
|
|
56
|
+
data: '__fn' in req ? RESPONSES[req.__fn] : {},
|
|
57
|
+
status: 200,
|
|
58
|
+
statusText: 'OK',
|
|
59
|
+
headers: {}
|
|
60
|
+
}));
|
|
61
|
+
const httpClient = {
|
|
62
|
+
request
|
|
63
|
+
};
|
|
64
|
+
const withErrorHandler = jest.fn(fn => fn());
|
|
65
|
+
const errorHandler = {
|
|
66
|
+
withErrorHandler
|
|
67
|
+
};
|
|
68
|
+
return {
|
|
69
|
+
httpClient,
|
|
70
|
+
errorHandler,
|
|
71
|
+
request
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
describe('hubJobClient', () => {
|
|
75
|
+
let client;
|
|
76
|
+
let request;
|
|
77
|
+
beforeEach(() => {
|
|
78
|
+
const deps = makeDeps();
|
|
79
|
+
request = deps.request;
|
|
80
|
+
client = (0, _hubJobClient.createHubJobClient)(deps);
|
|
81
|
+
});
|
|
82
|
+
describe('uploadFile', () => {
|
|
83
|
+
const file = new File(['a,b\n1,2'], 'data.csv', {
|
|
84
|
+
type: 'text/csv'
|
|
85
|
+
});
|
|
86
|
+
it('reserves an upload url, PUTs the file, and returns the fileId', async () => {
|
|
87
|
+
const fileId = await client.uploadFile(file);
|
|
88
|
+
expect(fileId).toBe('file-1');
|
|
89
|
+
// createFileUploadUrl called with the file name
|
|
90
|
+
expect(request).toHaveBeenCalledWith(expect.objectContaining({
|
|
91
|
+
__fn: 'createFileUploadUrl',
|
|
92
|
+
payload: {
|
|
93
|
+
fileName: 'data.csv'
|
|
94
|
+
}
|
|
95
|
+
}));
|
|
96
|
+
// PUT the file to the returned signed url via httpClient.request
|
|
97
|
+
expect(request).toHaveBeenCalledWith({
|
|
98
|
+
url: 'https://upload.example/abc',
|
|
99
|
+
method: 'PUT',
|
|
100
|
+
data: file
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
it('rejects when the upload PUT fails (client throws on non-2xx)', async () => {
|
|
104
|
+
request.mockImplementation(async req => {
|
|
105
|
+
if (req.method === 'PUT') {
|
|
106
|
+
throw new Error('Request failed with status code 500');
|
|
107
|
+
}
|
|
108
|
+
return {
|
|
109
|
+
data: RESPONSES[req.__fn],
|
|
110
|
+
status: 200,
|
|
111
|
+
statusText: 'OK',
|
|
112
|
+
headers: {}
|
|
113
|
+
};
|
|
114
|
+
});
|
|
115
|
+
await expect(client.uploadFile(file)).rejects.toThrow('Request failed with status code 500');
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
describe('submitImportJob', () => {
|
|
119
|
+
const destination = {
|
|
120
|
+
wixDataCollection: {
|
|
121
|
+
collectionId: 'Movies'
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
it('returns the job id and sends a CSV file source with the destination', async () => {
|
|
125
|
+
const jobId = await client.submitImportJob({
|
|
126
|
+
fileId: 'file-1',
|
|
127
|
+
destination,
|
|
128
|
+
name: 'data.csv'
|
|
129
|
+
});
|
|
130
|
+
expect(jobId).toBe('job-1');
|
|
131
|
+
expect(request).toHaveBeenCalledWith(expect.objectContaining({
|
|
132
|
+
__fn: 'submitJob',
|
|
133
|
+
payload: expect.objectContaining({
|
|
134
|
+
name: 'data.csv',
|
|
135
|
+
source: {
|
|
136
|
+
file: {
|
|
137
|
+
fileId: 'file-1',
|
|
138
|
+
format: _types.FileFormat.CSV
|
|
139
|
+
}
|
|
140
|
+
},
|
|
141
|
+
destination
|
|
142
|
+
})
|
|
143
|
+
}));
|
|
144
|
+
});
|
|
145
|
+
it('throws when no job id is returned', async () => {
|
|
146
|
+
RESPONSES.submitJob = {
|
|
147
|
+
job: {}
|
|
148
|
+
};
|
|
149
|
+
await expect(client.submitImportJob({
|
|
150
|
+
fileId: 'file-1',
|
|
151
|
+
destination
|
|
152
|
+
})).rejects.toThrow('submitJob returned no job id');
|
|
153
|
+
RESPONSES.submitJob = {
|
|
154
|
+
job: {
|
|
155
|
+
id: 'job-1'
|
|
156
|
+
}
|
|
157
|
+
};
|
|
158
|
+
});
|
|
159
|
+
});
|
|
160
|
+
describe('getJob', () => {
|
|
161
|
+
it('returns the job', async () => {
|
|
162
|
+
const job = await client.getJob('job-1');
|
|
163
|
+
expect(job).toEqual({
|
|
164
|
+
id: 'job-1',
|
|
165
|
+
status: 'IN_PROGRESS'
|
|
166
|
+
});
|
|
167
|
+
expect(request).toHaveBeenCalledWith(expect.objectContaining({
|
|
168
|
+
__fn: 'getJob',
|
|
169
|
+
payload: {
|
|
170
|
+
jobId: 'job-1'
|
|
171
|
+
}
|
|
172
|
+
}));
|
|
173
|
+
});
|
|
174
|
+
});
|
|
175
|
+
describe('terminateJob', () => {
|
|
176
|
+
it('requests termination by job id', async () => {
|
|
177
|
+
await client.terminateJob('job-1');
|
|
178
|
+
expect(request).toHaveBeenCalledWith(expect.objectContaining({
|
|
179
|
+
__fn: 'terminateJob',
|
|
180
|
+
payload: {
|
|
181
|
+
jobId: 'job-1'
|
|
182
|
+
}
|
|
183
|
+
}));
|
|
184
|
+
});
|
|
185
|
+
});
|
|
186
|
+
describe('listMovementLogs', () => {
|
|
187
|
+
it('returns the logs response', async () => {
|
|
188
|
+
const res = await client.listMovementLogs('job-1');
|
|
189
|
+
expect(res.logs).toEqual([{
|
|
190
|
+
sourceItemId: '3'
|
|
191
|
+
}]);
|
|
192
|
+
});
|
|
193
|
+
it('passes paging when provided', async () => {
|
|
194
|
+
await client.listMovementLogs('job-1', {
|
|
195
|
+
limit: 50
|
|
196
|
+
});
|
|
197
|
+
expect(request).toHaveBeenCalledWith(expect.objectContaining({
|
|
198
|
+
__fn: 'listMovementLogs',
|
|
199
|
+
payload: {
|
|
200
|
+
jobId: 'job-1',
|
|
201
|
+
paging: {
|
|
202
|
+
limit: 50
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}));
|
|
206
|
+
});
|
|
207
|
+
});
|
|
208
|
+
});
|
|
209
|
+
//# sourceMappingURL=hubJobClient.spec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["_hubJobClient","require","_types","jest","mock","createFileUploadUrl","fn","payload","__fn","submitJob","getJob","terminateJob","listMovementLogs","RESPONSES","uploadUrl","fileId","job","id","status","logs","sourceItemId","makeDeps","request","req","data","statusText","headers","httpClient","withErrorHandler","errorHandler","describe","client","beforeEach","deps","createHubJobClient","file","File","type","it","uploadFile","expect","toBe","toHaveBeenCalledWith","objectContaining","fileName","url","method","mockImplementation","Error","rejects","toThrow","destination","wixDataCollection","collectionId","jobId","submitImportJob","name","source","format","FileFormat","CSV","toEqual","res","limit","paging"],"sources":["../../../../../src/state/ImportState/__tests__/hubJobClient.spec.ts"],"sourcesContent":["import { createHubJobClient, HubJobClient } from '../hubJobClient';\nimport { FileFormat } from '@wix/bex-utils/@wix/ambassador-hub-v1-job/types';\nimport type { PatternsErrorHandler } from '../../../model';\nimport type { HttpClient } from '../../../model/httpClient';\n\n// Mock the SDK http factories so each returns an identifiable descriptor; the\n// mocked httpClient then routes on `__fn` and we can assert the payloads.\njest.mock('@wix/bex-utils/@wix/ambassador-hub-v1-job/http', () => ({\n createFileUploadUrl: jest.fn((payload) => ({\n __fn: 'createFileUploadUrl',\n payload,\n })),\n submitJob: jest.fn((payload) => ({ __fn: 'submitJob', payload })),\n getJob: jest.fn((payload) => ({ __fn: 'getJob', payload })),\n terminateJob: jest.fn((payload) => ({ __fn: 'terminateJob', payload })),\n listMovementLogs: jest.fn((payload) => ({\n __fn: 'listMovementLogs',\n payload,\n })),\n}));\n\ntype Descriptor = { __fn: string; payload: any };\n\nconst RESPONSES: Record<string, unknown> = {\n createFileUploadUrl: {\n uploadUrl: 'https://upload.example/abc',\n fileId: 'file-1',\n },\n submitJob: { job: { id: 'job-1' } },\n getJob: { job: { id: 'job-1', status: 'IN_PROGRESS' } },\n terminateJob: {},\n listMovementLogs: { logs: [{ sourceItemId: '3' }] },\n};\n\nfunction makeDeps() {\n // Routes RPC factories on `__fn`; the file upload comes in as a raw request\n // config (no `__fn`) and just needs to resolve.\n const request = jest.fn(async (req: Descriptor | { method?: string }) => ({\n data: '__fn' in req ? RESPONSES[req.__fn] : {},\n status: 200,\n statusText: 'OK',\n headers: {},\n }));\n const httpClient = { request } as unknown as HttpClient;\n\n const withErrorHandler = jest.fn((fn: () => Promise<unknown>) => fn());\n const errorHandler = { withErrorHandler } as unknown as PatternsErrorHandler;\n\n return { httpClient, errorHandler, request };\n}\n\ndescribe('hubJobClient', () => {\n let client: HubJobClient;\n let request: jest.Mock;\n\n beforeEach(() => {\n const deps = makeDeps();\n request = deps.request;\n client = createHubJobClient(deps);\n });\n\n describe('uploadFile', () => {\n const file = new File(['a,b\\n1,2'], 'data.csv', { type: 'text/csv' });\n\n it('reserves an upload url, PUTs the file, and returns the fileId', async () => {\n const fileId = await client.uploadFile(file);\n\n expect(fileId).toBe('file-1');\n // createFileUploadUrl called with the file name\n expect(request).toHaveBeenCalledWith(\n expect.objectContaining({\n __fn: 'createFileUploadUrl',\n payload: { fileName: 'data.csv' },\n }),\n );\n // PUT the file to the returned signed url via httpClient.request\n expect(request).toHaveBeenCalledWith({\n url: 'https://upload.example/abc',\n method: 'PUT',\n data: file,\n });\n });\n\n it('rejects when the upload PUT fails (client throws on non-2xx)', async () => {\n request.mockImplementation(async (req: any) => {\n if (req.method === 'PUT') {\n throw new Error('Request failed with status code 500');\n }\n return {\n data: RESPONSES[req.__fn],\n status: 200,\n statusText: 'OK',\n headers: {},\n };\n });\n await expect(client.uploadFile(file)).rejects.toThrow(\n 'Request failed with status code 500',\n );\n });\n });\n\n describe('submitImportJob', () => {\n const destination = { wixDataCollection: { collectionId: 'Movies' } };\n\n it('returns the job id and sends a CSV file source with the destination', async () => {\n const jobId = await client.submitImportJob({\n fileId: 'file-1',\n destination,\n name: 'data.csv',\n });\n\n expect(jobId).toBe('job-1');\n expect(request).toHaveBeenCalledWith(\n expect.objectContaining({\n __fn: 'submitJob',\n payload: expect.objectContaining({\n name: 'data.csv',\n source: { file: { fileId: 'file-1', format: FileFormat.CSV } },\n destination,\n }),\n }),\n );\n });\n\n it('throws when no job id is returned', async () => {\n RESPONSES.submitJob = { job: {} };\n await expect(\n client.submitImportJob({ fileId: 'file-1', destination }),\n ).rejects.toThrow('submitJob returned no job id');\n RESPONSES.submitJob = { job: { id: 'job-1' } };\n });\n });\n\n describe('getJob', () => {\n it('returns the job', async () => {\n const job = await client.getJob('job-1');\n expect(job).toEqual({ id: 'job-1', status: 'IN_PROGRESS' });\n expect(request).toHaveBeenCalledWith(\n expect.objectContaining({\n __fn: 'getJob',\n payload: { jobId: 'job-1' },\n }),\n );\n });\n });\n\n describe('terminateJob', () => {\n it('requests termination by job id', async () => {\n await client.terminateJob('job-1');\n expect(request).toHaveBeenCalledWith(\n expect.objectContaining({\n __fn: 'terminateJob',\n payload: { jobId: 'job-1' },\n }),\n );\n });\n });\n\n describe('listMovementLogs', () => {\n it('returns the logs response', async () => {\n const res = await client.listMovementLogs('job-1');\n expect(res.logs).toEqual([{ sourceItemId: '3' }]);\n });\n\n it('passes paging when provided', async () => {\n await client.listMovementLogs('job-1', { limit: 50 });\n expect(request).toHaveBeenCalledWith(\n expect.objectContaining({\n __fn: 'listMovementLogs',\n payload: { jobId: 'job-1', paging: { limit: 50 } },\n }),\n );\n });\n });\n});\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _importJobPoller = require("../importJobPoller");
|
|
4
|
+
var _types = require("@wix/bex-utils/@wix/ambassador-hub-v1-job/types");
|
|
5
|
+
const noWait = () => Promise.resolve();
|
|
6
|
+
describe('isTerminalJobStatus', () => {
|
|
7
|
+
it('is true for completed / partially successful / failed', () => {
|
|
8
|
+
expect((0, _importJobPoller.isTerminalJobStatus)(_types.JobStatus.COMPLETED)).toBe(true);
|
|
9
|
+
expect((0, _importJobPoller.isTerminalJobStatus)(_types.JobStatus.PARTIALLY_SUCCESSFUL)).toBe(true);
|
|
10
|
+
expect((0, _importJobPoller.isTerminalJobStatus)(_types.JobStatus.FAILED)).toBe(true);
|
|
11
|
+
});
|
|
12
|
+
it('is false for in-progress / initializing / undefined', () => {
|
|
13
|
+
expect((0, _importJobPoller.isTerminalJobStatus)(_types.JobStatus.IN_PROGRESS)).toBe(false);
|
|
14
|
+
expect((0, _importJobPoller.isTerminalJobStatus)(_types.JobStatus.INITIALIZING)).toBe(false);
|
|
15
|
+
expect((0, _importJobPoller.isTerminalJobStatus)(undefined)).toBe(false);
|
|
16
|
+
});
|
|
17
|
+
});
|
|
18
|
+
describe('pollImportJob', () => {
|
|
19
|
+
it('polls until terminal and returns the terminal job', async () => {
|
|
20
|
+
const jobs = [{
|
|
21
|
+
id: 'j',
|
|
22
|
+
status: _types.JobStatus.INITIALIZING
|
|
23
|
+
}, {
|
|
24
|
+
id: 'j',
|
|
25
|
+
status: _types.JobStatus.IN_PROGRESS
|
|
26
|
+
}, {
|
|
27
|
+
id: 'j',
|
|
28
|
+
status: _types.JobStatus.COMPLETED
|
|
29
|
+
}];
|
|
30
|
+
const getJob = jest.fn(async () => jobs.shift());
|
|
31
|
+
const result = await (0, _importJobPoller.pollImportJob)(getJob, 'j', {
|
|
32
|
+
wait: noWait
|
|
33
|
+
});
|
|
34
|
+
expect(getJob).toHaveBeenCalledTimes(3);
|
|
35
|
+
expect(result == null ? void 0 : result.status).toBe(_types.JobStatus.COMPLETED);
|
|
36
|
+
});
|
|
37
|
+
it('reports progress for every fetched job', async () => {
|
|
38
|
+
const jobs = [{
|
|
39
|
+
id: 'j',
|
|
40
|
+
status: _types.JobStatus.IN_PROGRESS,
|
|
41
|
+
progress: {
|
|
42
|
+
current: 1
|
|
43
|
+
}
|
|
44
|
+
}, {
|
|
45
|
+
id: 'j',
|
|
46
|
+
status: _types.JobStatus.COMPLETED,
|
|
47
|
+
progress: {
|
|
48
|
+
current: 2
|
|
49
|
+
}
|
|
50
|
+
}];
|
|
51
|
+
const getJob = jest.fn(async () => jobs.shift());
|
|
52
|
+
const onProgress = jest.fn();
|
|
53
|
+
await (0, _importJobPoller.pollImportJob)(getJob, 'j', {
|
|
54
|
+
wait: noWait,
|
|
55
|
+
onProgress
|
|
56
|
+
});
|
|
57
|
+
expect(onProgress).toHaveBeenCalledTimes(2);
|
|
58
|
+
expect(onProgress.mock.calls[1][0].progress.current).toBe(2);
|
|
59
|
+
});
|
|
60
|
+
it('stops and returns null when cancelled before the first poll', async () => {
|
|
61
|
+
const getJob = jest.fn();
|
|
62
|
+
const result = await (0, _importJobPoller.pollImportJob)(getJob, 'j', {
|
|
63
|
+
wait: noWait,
|
|
64
|
+
isCancelled: () => true
|
|
65
|
+
});
|
|
66
|
+
expect(result).toBeNull();
|
|
67
|
+
expect(getJob).not.toHaveBeenCalled();
|
|
68
|
+
});
|
|
69
|
+
it('stops between polls when cancelled mid-run', async () => {
|
|
70
|
+
let cancelled = false;
|
|
71
|
+
const getJob = jest.fn(async () => {
|
|
72
|
+
cancelled = true; // cancel after the first fetch
|
|
73
|
+
return {
|
|
74
|
+
id: 'j',
|
|
75
|
+
status: _types.JobStatus.IN_PROGRESS
|
|
76
|
+
};
|
|
77
|
+
});
|
|
78
|
+
const result = await (0, _importJobPoller.pollImportJob)(getJob, 'j', {
|
|
79
|
+
wait: noWait,
|
|
80
|
+
isCancelled: () => cancelled
|
|
81
|
+
});
|
|
82
|
+
expect(result).toBeNull();
|
|
83
|
+
expect(getJob).toHaveBeenCalledTimes(1);
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
//# sourceMappingURL=importJobPoller.spec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["_importJobPoller","require","_types","noWait","Promise","resolve","describe","it","expect","isTerminalJobStatus","JobStatus","COMPLETED","toBe","PARTIALLY_SUCCESSFUL","FAILED","IN_PROGRESS","INITIALIZING","undefined","jobs","id","status","getJob","jest","fn","shift","result","pollImportJob","wait","toHaveBeenCalledTimes","progress","current","onProgress","mock","calls","isCancelled","toBeNull","not","toHaveBeenCalled","cancelled"],"sources":["../../../../../src/state/ImportState/__tests__/importJobPoller.spec.ts"],"sourcesContent":["import { pollImportJob, isTerminalJobStatus } from '../importJobPoller';\nimport {\n JobStatus,\n type DataMovementJob,\n} from '@wix/bex-utils/@wix/ambassador-hub-v1-job/types';\n\nconst noWait = () => Promise.resolve();\n\ndescribe('isTerminalJobStatus', () => {\n it('is true for completed / partially successful / failed', () => {\n expect(isTerminalJobStatus(JobStatus.COMPLETED)).toBe(true);\n expect(isTerminalJobStatus(JobStatus.PARTIALLY_SUCCESSFUL)).toBe(true);\n expect(isTerminalJobStatus(JobStatus.FAILED)).toBe(true);\n });\n\n it('is false for in-progress / initializing / undefined', () => {\n expect(isTerminalJobStatus(JobStatus.IN_PROGRESS)).toBe(false);\n expect(isTerminalJobStatus(JobStatus.INITIALIZING)).toBe(false);\n expect(isTerminalJobStatus(undefined)).toBe(false);\n });\n});\n\ndescribe('pollImportJob', () => {\n it('polls until terminal and returns the terminal job', async () => {\n const jobs: DataMovementJob[] = [\n { id: 'j', status: JobStatus.INITIALIZING },\n { id: 'j', status: JobStatus.IN_PROGRESS },\n { id: 'j', status: JobStatus.COMPLETED },\n ];\n const getJob = jest.fn(async () => jobs.shift()!);\n\n const result = await pollImportJob(getJob, 'j', { wait: noWait });\n\n expect(getJob).toHaveBeenCalledTimes(3);\n expect(result?.status).toBe(JobStatus.COMPLETED);\n });\n\n it('reports progress for every fetched job', async () => {\n const jobs: DataMovementJob[] = [\n { id: 'j', status: JobStatus.IN_PROGRESS, progress: { current: 1 } },\n { id: 'j', status: JobStatus.COMPLETED, progress: { current: 2 } },\n ];\n const getJob = jest.fn(async () => jobs.shift()!);\n const onProgress = jest.fn();\n\n await pollImportJob(getJob, 'j', { wait: noWait, onProgress });\n\n expect(onProgress).toHaveBeenCalledTimes(2);\n expect(onProgress.mock.calls[1][0].progress.current).toBe(2);\n });\n\n it('stops and returns null when cancelled before the first poll', async () => {\n const getJob = jest.fn();\n const result = await pollImportJob(getJob, 'j', {\n wait: noWait,\n isCancelled: () => true,\n });\n expect(result).toBeNull();\n expect(getJob).not.toHaveBeenCalled();\n });\n\n it('stops between polls when cancelled mid-run', async () => {\n let cancelled = false;\n const getJob = jest.fn(async () => {\n cancelled = true; // cancel after the first fetch\n return { id: 'j', status: JobStatus.IN_PROGRESS } as DataMovementJob;\n });\n\n const result = await pollImportJob(getJob, 'j', {\n wait: noWait,\n isCancelled: () => cancelled,\n });\n\n expect(result).toBeNull();\n expect(getJob).toHaveBeenCalledTimes(1);\n });\n});\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _parseCsv = require("../parseCsv");
|
|
4
|
+
describe('parseCsv', () => {
|
|
5
|
+
it('splits headers from data rows', () => {
|
|
6
|
+
const result = (0, _parseCsv.parseCsv)('a,b\n1,2\n3,4');
|
|
7
|
+
expect(result).toEqual({
|
|
8
|
+
headers: ['a', 'b'],
|
|
9
|
+
rows: [['1', '2'], ['3', '4']]
|
|
10
|
+
});
|
|
11
|
+
});
|
|
12
|
+
it('drops fully empty rows', () => {
|
|
13
|
+
const result = (0, _parseCsv.parseCsv)('a,b\n1,2\n\n3,4');
|
|
14
|
+
expect(result).toEqual({
|
|
15
|
+
headers: ['a', 'b'],
|
|
16
|
+
rows: [['1', '2'], ['3', '4']]
|
|
17
|
+
});
|
|
18
|
+
});
|
|
19
|
+
it('returns empty headers/rows for empty content', () => {
|
|
20
|
+
expect((0, _parseCsv.parseCsv)('')).toEqual({
|
|
21
|
+
headers: [],
|
|
22
|
+
rows: []
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
describe('parseCsvSample', () => {
|
|
27
|
+
it('returns the header plus at most maxRows data rows', () => {
|
|
28
|
+
const content = 'a,b\n1,1\n2,2\n3,3\n4,4\n5,5';
|
|
29
|
+
const result = (0, _parseCsv.parseCsvSample)(content, 2);
|
|
30
|
+
expect(result).toEqual({
|
|
31
|
+
headers: ['a', 'b'],
|
|
32
|
+
rows: [['1', '1'], ['2', '2']]
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
it('returns all rows when fewer than maxRows exist', () => {
|
|
36
|
+
const result = (0, _parseCsv.parseCsvSample)('a,b\n1,1', 10);
|
|
37
|
+
expect(result).toEqual({
|
|
38
|
+
headers: ['a', 'b'],
|
|
39
|
+
rows: [['1', '1']]
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
describe('unparseCsv', () => {
|
|
44
|
+
it('round-trips through parseCsv', () => {
|
|
45
|
+
const csv = (0, _parseCsv.unparseCsv)(['title', 'price'], [['Apple', '1'], ['Pear, Inc.', '2']]);
|
|
46
|
+
expect((0, _parseCsv.parseCsv)(csv)).toEqual({
|
|
47
|
+
headers: ['title', 'price'],
|
|
48
|
+
rows: [['Apple', '1'], ['Pear, Inc.', '2']]
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
//# sourceMappingURL=parseCsv.spec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["_parseCsv","require","describe","it","result","parseCsv","expect","toEqual","headers","rows","content","parseCsvSample","csv","unparseCsv"],"sources":["../../../../../src/state/ImportState/__tests__/parseCsv.spec.ts"],"sourcesContent":["import { parseCsv, parseCsvSample, unparseCsv } from '../parseCsv';\n\ndescribe('parseCsv', () => {\n it('splits headers from data rows', () => {\n const result = parseCsv('a,b\\n1,2\\n3,4');\n expect(result).toEqual({\n headers: ['a', 'b'],\n rows: [\n ['1', '2'],\n ['3', '4'],\n ],\n });\n });\n\n it('drops fully empty rows', () => {\n const result = parseCsv('a,b\\n1,2\\n\\n3,4');\n expect(result).toEqual({\n headers: ['a', 'b'],\n rows: [\n ['1', '2'],\n ['3', '4'],\n ],\n });\n });\n\n it('returns empty headers/rows for empty content', () => {\n expect(parseCsv('')).toEqual({ headers: [], rows: [] });\n });\n});\n\ndescribe('parseCsvSample', () => {\n it('returns the header plus at most maxRows data rows', () => {\n const content = 'a,b\\n1,1\\n2,2\\n3,3\\n4,4\\n5,5';\n const result = parseCsvSample(content, 2);\n expect(result).toEqual({\n headers: ['a', 'b'],\n rows: [\n ['1', '1'],\n ['2', '2'],\n ],\n });\n });\n\n it('returns all rows when fewer than maxRows exist', () => {\n const result = parseCsvSample('a,b\\n1,1', 10);\n expect(result).toEqual({ headers: ['a', 'b'], rows: [['1', '1']] });\n });\n});\n\ndescribe('unparseCsv', () => {\n it('round-trips through parseCsv', () => {\n const csv = unparseCsv(\n ['title', 'price'],\n [\n ['Apple', '1'],\n ['Pear, Inc.', '2'],\n ],\n );\n expect(parseCsv(csv)).toEqual({\n headers: ['title', 'price'],\n rows: [\n ['Apple', '1'],\n ['Pear, Inc.', '2'],\n ],\n });\n });\n});\n"],"mappings":";;AAAA,IAAAA,SAAA,GAAAC,OAAA;AAEAC,QAAQ,CAAC,UAAU,EAAE,MAAM;EACzBC,EAAE,CAAC,+BAA+B,EAAE,MAAM;IACxC,MAAMC,MAAM,GAAG,IAAAC,kBAAQ,EAAC,eAAe,CAAC;IACxCC,MAAM,CAACF,MAAM,CAAC,CAACG,OAAO,CAAC;MACrBC,OAAO,EAAE,CAAC,GAAG,EAAE,GAAG,CAAC;MACnBC,IAAI,EAAE,CACJ,CAAC,GAAG,EAAE,GAAG,CAAC,EACV,CAAC,GAAG,EAAE,GAAG,CAAC;IAEd,CAAC,CAAC;EACJ,CAAC,CAAC;EAEFN,EAAE,CAAC,wBAAwB,EAAE,MAAM;IACjC,MAAMC,MAAM,GAAG,IAAAC,kBAAQ,EAAC,iBAAiB,CAAC;IAC1CC,MAAM,CAACF,MAAM,CAAC,CAACG,OAAO,CAAC;MACrBC,OAAO,EAAE,CAAC,GAAG,EAAE,GAAG,CAAC;MACnBC,IAAI,EAAE,CACJ,CAAC,GAAG,EAAE,GAAG,CAAC,EACV,CAAC,GAAG,EAAE,GAAG,CAAC;IAEd,CAAC,CAAC;EACJ,CAAC,CAAC;EAEFN,EAAE,CAAC,8CAA8C,EAAE,MAAM;IACvDG,MAAM,CAAC,IAAAD,kBAAQ,EAAC,EAAE,CAAC,CAAC,CAACE,OAAO,CAAC;MAAEC,OAAO,EAAE,EAAE;MAAEC,IAAI,EAAE;IAAG,CAAC,CAAC;EACzD,CAAC,CAAC;AACJ,CAAC,CAAC;AAEFP,QAAQ,CAAC,gBAAgB,EAAE,MAAM;EAC/BC,EAAE,CAAC,mDAAmD,EAAE,MAAM;IAC5D,MAAMO,OAAO,GAAG,8BAA8B;IAC9C,MAAMN,MAAM,GAAG,IAAAO,wBAAc,EAACD,OAAO,EAAE,CAAC,CAAC;IACzCJ,MAAM,CAACF,MAAM,CAAC,CAACG,OAAO,CAAC;MACrBC,OAAO,EAAE,CAAC,GAAG,EAAE,GAAG,CAAC;MACnBC,IAAI,EAAE,CACJ,CAAC,GAAG,EAAE,GAAG,CAAC,EACV,CAAC,GAAG,EAAE,GAAG,CAAC;IAEd,CAAC,CAAC;EACJ,CAAC,CAAC;EAEFN,EAAE,CAAC,gDAAgD,EAAE,MAAM;IACzD,MAAMC,MAAM,GAAG,IAAAO,wBAAc,EAAC,UAAU,EAAE,EAAE,CAAC;IAC7CL,MAAM,CAACF,MAAM,CAAC,CAACG,OAAO,CAAC;MAAEC,OAAO,EAAE,CAAC,GAAG,EAAE,GAAG,CAAC;MAAEC,IAAI,EAAE,CAAC,CAAC,GAAG,EAAE,GAAG,CAAC;IAAE,CAAC,CAAC;EACrE,CAAC,CAAC;AACJ,CAAC,CAAC;AAEFP,QAAQ,CAAC,YAAY,EAAE,MAAM;EAC3BC,EAAE,CAAC,8BAA8B,EAAE,MAAM;IACvC,MAAMS,GAAG,GAAG,IAAAC,oBAAU,EACpB,CAAC,OAAO,EAAE,OAAO,CAAC,EAClB,CACE,CAAC,OAAO,EAAE,GAAG,CAAC,EACd,CAAC,YAAY,EAAE,GAAG,CAAC,CAEvB,CAAC;IACDP,MAAM,CAAC,IAAAD,kBAAQ,EAACO,GAAG,CAAC,CAAC,CAACL,OAAO,CAAC;MAC5BC,OAAO,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC;MAC3BC,IAAI,EAAE,CACJ,CAAC,OAAO,EAAE,GAAG,CAAC,EACd,CAAC,YAAY,EAAE,GAAG,CAAC;IAEvB,CAAC,CAAC;EACJ,CAAC,CAAC;AACJ,CAAC,CAAC","ignoreList":[]}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _rewriteCsv = require("../rewriteCsv");
|
|
4
|
+
var _parseCsv = require("../parseCsv");
|
|
5
|
+
// Parse the rewritten output back so assertions are on logical rows, not on
|
|
6
|
+
// exact serialization details.
|
|
7
|
+
function reparse(content) {
|
|
8
|
+
const result = (0, _parseCsv.parseCsv)(content);
|
|
9
|
+
if ('code' in result) {
|
|
10
|
+
throw new Error('rewrite produced unparseable CSV');
|
|
11
|
+
}
|
|
12
|
+
return result;
|
|
13
|
+
}
|
|
14
|
+
describe('rewriteCsvForImport', () => {
|
|
15
|
+
it('renames headers to the destination field keys, keeping the data', () => {
|
|
16
|
+
const content = 'Name,Price\nApple,1\nPear,2';
|
|
17
|
+
const out = reparse((0, _rewriteCsv.rewriteCsvForImport)(content, {
|
|
18
|
+
Name: 'title',
|
|
19
|
+
Price: 'price'
|
|
20
|
+
}));
|
|
21
|
+
expect(out.headers).toEqual(['title', 'price']);
|
|
22
|
+
expect(out.rows).toEqual([['Apple', '1'], ['Pear', '2']]);
|
|
23
|
+
});
|
|
24
|
+
it('drops columns mapped to null', () => {
|
|
25
|
+
const content = 'Name,Price,Internal\nApple,1,x\nPear,2,y';
|
|
26
|
+
const out = reparse((0, _rewriteCsv.rewriteCsvForImport)(content, {
|
|
27
|
+
Name: 'title',
|
|
28
|
+
Price: 'price',
|
|
29
|
+
Internal: null
|
|
30
|
+
}));
|
|
31
|
+
expect(out.headers).toEqual(['title', 'price']);
|
|
32
|
+
expect(out.rows).toEqual([['Apple', '1'], ['Pear', '2']]);
|
|
33
|
+
});
|
|
34
|
+
it('drops columns absent from the mapping (treated as skip)', () => {
|
|
35
|
+
const content = 'Name,Extra\nApple,x';
|
|
36
|
+
const out = reparse((0, _rewriteCsv.rewriteCsvForImport)(content, {
|
|
37
|
+
Name: 'title'
|
|
38
|
+
}));
|
|
39
|
+
expect(out.headers).toEqual(['title']);
|
|
40
|
+
expect(out.rows).toEqual([['Apple']]);
|
|
41
|
+
});
|
|
42
|
+
it('keeps original column order', () => {
|
|
43
|
+
const content = 'A,B,C\n1,2,3';
|
|
44
|
+
const out = reparse((0, _rewriteCsv.rewriteCsvForImport)(content, {
|
|
45
|
+
A: 'a',
|
|
46
|
+
B: null,
|
|
47
|
+
C: 'c'
|
|
48
|
+
}));
|
|
49
|
+
expect(out.headers).toEqual(['a', 'c']);
|
|
50
|
+
expect(out.rows).toEqual([['1', '3']]);
|
|
51
|
+
});
|
|
52
|
+
it('fills missing trailing cells with empty strings', () => {
|
|
53
|
+
// second row is missing the Price cell
|
|
54
|
+
const content = 'Name,Price\nApple,1\nPear';
|
|
55
|
+
const out = reparse((0, _rewriteCsv.rewriteCsvForImport)(content, {
|
|
56
|
+
Name: 'title',
|
|
57
|
+
Price: 'price'
|
|
58
|
+
}));
|
|
59
|
+
expect(out.rows).toEqual([['Apple', '1'], ['Pear', '']]);
|
|
60
|
+
});
|
|
61
|
+
it('returns an empty result when nothing is mapped', () => {
|
|
62
|
+
const content = 'Name,Price\nApple,1';
|
|
63
|
+
const out = (0, _rewriteCsv.rewriteCsvForImport)(content, {
|
|
64
|
+
Name: null,
|
|
65
|
+
Price: null
|
|
66
|
+
});
|
|
67
|
+
// no headers, no rows
|
|
68
|
+
expect(reparse(out)).toEqual({
|
|
69
|
+
headers: [],
|
|
70
|
+
rows: []
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
it('quotes values containing commas so the round-trip is safe', () => {
|
|
74
|
+
const content = 'Name\n"Apple, Inc."';
|
|
75
|
+
const out = reparse((0, _rewriteCsv.rewriteCsvForImport)(content, {
|
|
76
|
+
Name: 'title'
|
|
77
|
+
}));
|
|
78
|
+
expect(out.rows).toEqual([['Apple, Inc.']]);
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
//# sourceMappingURL=rewriteCsv.spec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["_rewriteCsv","require","_parseCsv","reparse","content","result","parseCsv","Error","describe","it","out","rewriteCsvForImport","Name","Price","expect","headers","toEqual","rows","Internal","A","B","C"],"sources":["../../../../../src/state/ImportState/__tests__/rewriteCsv.spec.ts"],"sourcesContent":["import { rewriteCsvForImport } from '../rewriteCsv';\nimport { parseCsv, ParseCsvResult } from '../parseCsv';\n\n// Parse the rewritten output back so assertions are on logical rows, not on\n// exact serialization details.\nfunction reparse(content: string): ParseCsvResult {\n const result = parseCsv(content);\n if ('code' in result) {\n throw new Error('rewrite produced unparseable CSV');\n }\n return result;\n}\n\ndescribe('rewriteCsvForImport', () => {\n it('renames headers to the destination field keys, keeping the data', () => {\n const content = 'Name,Price\\nApple,1\\nPear,2';\n const out = reparse(\n rewriteCsvForImport(content, { Name: 'title', Price: 'price' }),\n );\n expect(out.headers).toEqual(['title', 'price']);\n expect(out.rows).toEqual([\n ['Apple', '1'],\n ['Pear', '2'],\n ]);\n });\n\n it('drops columns mapped to null', () => {\n const content = 'Name,Price,Internal\\nApple,1,x\\nPear,2,y';\n const out = reparse(\n rewriteCsvForImport(content, {\n Name: 'title',\n Price: 'price',\n Internal: null,\n }),\n );\n expect(out.headers).toEqual(['title', 'price']);\n expect(out.rows).toEqual([\n ['Apple', '1'],\n ['Pear', '2'],\n ]);\n });\n\n it('drops columns absent from the mapping (treated as skip)', () => {\n const content = 'Name,Extra\\nApple,x';\n const out = reparse(rewriteCsvForImport(content, { Name: 'title' }));\n expect(out.headers).toEqual(['title']);\n expect(out.rows).toEqual([['Apple']]);\n });\n\n it('keeps original column order', () => {\n const content = 'A,B,C\\n1,2,3';\n const out = reparse(\n rewriteCsvForImport(content, { A: 'a', B: null, C: 'c' }),\n );\n expect(out.headers).toEqual(['a', 'c']);\n expect(out.rows).toEqual([['1', '3']]);\n });\n\n it('fills missing trailing cells with empty strings', () => {\n // second row is missing the Price cell\n const content = 'Name,Price\\nApple,1\\nPear';\n const out = reparse(\n rewriteCsvForImport(content, { Name: 'title', Price: 'price' }),\n );\n expect(out.rows).toEqual([\n ['Apple', '1'],\n ['Pear', ''],\n ]);\n });\n\n it('returns an empty result when nothing is mapped', () => {\n const content = 'Name,Price\\nApple,1';\n const out = rewriteCsvForImport(content, { Name: null, Price: null });\n // no headers, no rows\n expect(reparse(out)).toEqual({ headers: [], rows: [] });\n });\n\n it('quotes values containing commas so the round-trip is safe', () => {\n const content = 'Name\\n\"Apple, Inc.\"';\n const out = reparse(rewriteCsvForImport(content, { Name: 'title' }));\n expect(out.rows).toEqual([['Apple, Inc.']]);\n });\n});\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
exports.__esModule = true;
|
|
4
|
+
exports.toFieldEdit = toFieldEdit;
|
|
5
|
+
exports.toNewFieldType = void 0;
|
|
6
|
+
// Reverse of the import detector's vocabulary: maps a schema field type
|
|
7
|
+
// (`PatternsFieldType`, uppercase) back to the import flow's `NewFieldType`. Only
|
|
8
|
+
// the types the field editor can produce need entries; anything else falls back
|
|
9
|
+
// to `text`.
|
|
10
|
+
const PATTERNS_TO_NEW_FIELD_TYPE = {
|
|
11
|
+
SHORT_TEXT: 'text',
|
|
12
|
+
LONG_TEXT: 'text',
|
|
13
|
+
NUMBER: 'number',
|
|
14
|
+
DATE: 'date',
|
|
15
|
+
DATETIME: 'date',
|
|
16
|
+
BOOLEAN: 'boolean',
|
|
17
|
+
URL: 'url',
|
|
18
|
+
IMAGE: 'image',
|
|
19
|
+
VIDEO: 'video',
|
|
20
|
+
DOCUMENT: 'document',
|
|
21
|
+
AUDIO: 'audio'
|
|
22
|
+
};
|
|
23
|
+
const toNewFieldType = type => PATTERNS_TO_NEW_FIELD_TYPE[type] ?? 'text';
|
|
24
|
+
|
|
25
|
+
// Converts an edited field spec (the field editor's output) into the per-column
|
|
26
|
+
// new-field type + config the import flow stores via `setNewFieldConfig`.
|
|
27
|
+
exports.toNewFieldType = toNewFieldType;
|
|
28
|
+
function toFieldEdit(field) {
|
|
29
|
+
const config = {
|
|
30
|
+
...(field.validation && {
|
|
31
|
+
validation: field.validation
|
|
32
|
+
}),
|
|
33
|
+
...(field.isPii && {
|
|
34
|
+
isPii: true
|
|
35
|
+
}),
|
|
36
|
+
...(field.helpText && {
|
|
37
|
+
helpText: field.helpText
|
|
38
|
+
})
|
|
39
|
+
};
|
|
40
|
+
return {
|
|
41
|
+
fieldType: toNewFieldType(field.type),
|
|
42
|
+
displayName: field.displayName,
|
|
43
|
+
config: Object.keys(config).length > 0 ? config : undefined
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=fieldEdit.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["PATTERNS_TO_NEW_FIELD_TYPE","SHORT_TEXT","LONG_TEXT","NUMBER","DATE","DATETIME","BOOLEAN","URL","IMAGE","VIDEO","DOCUMENT","AUDIO","toNewFieldType","type","exports","toFieldEdit","field","config","validation","isPii","helpText","fieldType","displayName","Object","keys","length","undefined"],"sources":["../../../../src/state/ImportState/fieldEdit.ts"],"sourcesContent":["import type { FieldSpec, PatternsFieldType } from '../../types/Field';\nimport type { NewFieldType } from './detectFieldType';\nimport type { NewFieldConfig } from './ImportState';\n\n// Reverse of the import detector's vocabulary: maps a schema field type\n// (`PatternsFieldType`, uppercase) back to the import flow's `NewFieldType`. Only\n// the types the field editor can produce need entries; anything else falls back\n// to `text`.\nconst PATTERNS_TO_NEW_FIELD_TYPE: Partial<\n Record<PatternsFieldType, NewFieldType>\n> = {\n SHORT_TEXT: 'text',\n LONG_TEXT: 'text',\n NUMBER: 'number',\n DATE: 'date',\n DATETIME: 'date',\n BOOLEAN: 'boolean',\n URL: 'url',\n IMAGE: 'image',\n VIDEO: 'video',\n DOCUMENT: 'document',\n AUDIO: 'audio',\n};\n\nexport const toNewFieldType = (type: PatternsFieldType): NewFieldType =>\n PATTERNS_TO_NEW_FIELD_TYPE[type] ?? 'text';\n\n// Converts an edited field spec (the field editor's output) into the per-column\n// new-field type + config the import flow stores via `setNewFieldConfig`.\nexport function toFieldEdit(field: FieldSpec): {\n fieldType: NewFieldType;\n displayName: string;\n config?: NewFieldConfig;\n} {\n const config: NewFieldConfig = {\n ...(field.validation && { validation: field.validation }),\n ...(field.isPii && { isPii: true }),\n ...(field.helpText && { helpText: field.helpText }),\n };\n return {\n fieldType: toNewFieldType(field.type),\n displayName: field.displayName,\n config: Object.keys(config).length > 0 ? config : undefined,\n };\n}\n"],"mappings":";;;;;AAIA;AACA;AACA;AACA;AACA,MAAMA,0BAEL,GAAG;EACFC,UAAU,EAAE,MAAM;EAClBC,SAAS,EAAE,MAAM;EACjBC,MAAM,EAAE,QAAQ;EAChBC,IAAI,EAAE,MAAM;EACZC,QAAQ,EAAE,MAAM;EAChBC,OAAO,EAAE,SAAS;EAClBC,GAAG,EAAE,KAAK;EACVC,KAAK,EAAE,OAAO;EACdC,KAAK,EAAE,OAAO;EACdC,QAAQ,EAAE,UAAU;EACpBC,KAAK,EAAE;AACT,CAAC;AAEM,MAAMC,cAAc,GAAIC,IAAuB,IACpDb,0BAA0B,CAACa,IAAI,CAAC,IAAI,MAAM;;AAE5C;AACA;AAAAC,OAAA,CAAAF,cAAA,GAAAA,cAAA;AACO,SAASG,WAAWA,CAACC,KAAgB,EAI1C;EACA,MAAMC,MAAsB,GAAG;IAC7B,IAAID,KAAK,CAACE,UAAU,IAAI;MAAEA,UAAU,EAAEF,KAAK,CAACE;IAAW,CAAC,CAAC;IACzD,IAAIF,KAAK,CAACG,KAAK,IAAI;MAAEA,KAAK,EAAE;IAAK,CAAC,CAAC;IACnC,IAAIH,KAAK,CAACI,QAAQ,IAAI;MAAEA,QAAQ,EAAEJ,KAAK,CAACI;IAAS,CAAC;EACpD,CAAC;EACD,OAAO;IACLC,SAAS,EAAET,cAAc,CAACI,KAAK,CAACH,IAAI,CAAC;IACrCS,WAAW,EAAEN,KAAK,CAACM,WAAW;IAC9BL,MAAM,EAAEM,MAAM,CAACC,IAAI,CAACP,MAAM,CAAC,CAACQ,MAAM,GAAG,CAAC,GAAGR,MAAM,GAAGS;EACpD,CAAC;AACH","ignoreList":[]}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
exports.__esModule = true;
|
|
4
|
+
exports.createHubJobClient = createHubJobClient;
|
|
5
|
+
var _http = require("@wix/bex-utils/@wix/ambassador-hub-v1-job/http");
|
|
6
|
+
var _types = require("@wix/bex-utils/@wix/ambassador-hub-v1-job/types");
|
|
7
|
+
/**
|
|
8
|
+
* Thin wrapper over the Hub Job (data-movement) API used by the server-side
|
|
9
|
+
* import. Each call goes through `errorHandler.withErrorHandler` and the
|
|
10
|
+
* container's `httpClient`, mirroring `ExportState`.
|
|
11
|
+
*
|
|
12
|
+
* Holds no observable state — it's a standalone unit consumed by `ImportState`.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
function createHubJobClient({
|
|
16
|
+
httpClient,
|
|
17
|
+
errorHandler
|
|
18
|
+
}) {
|
|
19
|
+
// Empty error-code map = default handling. Specific codes/messages need
|
|
20
|
+
// translation keys and are deferred to the translations task.
|
|
21
|
+
return {
|
|
22
|
+
async uploadFile(file) {
|
|
23
|
+
const {
|
|
24
|
+
data: {
|
|
25
|
+
uploadUrl,
|
|
26
|
+
fileId
|
|
27
|
+
}
|
|
28
|
+
} = await errorHandler.withErrorHandler(() => httpClient.request((0, _http.createFileUploadUrl)({
|
|
29
|
+
fileName: file.name
|
|
30
|
+
})), {});
|
|
31
|
+
if (!uploadUrl || !fileId) {
|
|
32
|
+
throw new Error('createFileUploadUrl returned no uploadUrl/fileId');
|
|
33
|
+
}
|
|
34
|
+
await errorHandler.withErrorHandler(() => httpClient.request({
|
|
35
|
+
url: uploadUrl,
|
|
36
|
+
method: 'PUT',
|
|
37
|
+
data: file
|
|
38
|
+
}), {});
|
|
39
|
+
return fileId;
|
|
40
|
+
},
|
|
41
|
+
async submitImportJob({
|
|
42
|
+
fileId,
|
|
43
|
+
destination,
|
|
44
|
+
name,
|
|
45
|
+
fileFormat = _types.FileFormat.CSV,
|
|
46
|
+
transformations
|
|
47
|
+
}) {
|
|
48
|
+
const {
|
|
49
|
+
data: {
|
|
50
|
+
job
|
|
51
|
+
}
|
|
52
|
+
} = await errorHandler.withErrorHandler(() => httpClient.request((0, _http.submitJob)({
|
|
53
|
+
name,
|
|
54
|
+
source: {
|
|
55
|
+
file: {
|
|
56
|
+
fileId,
|
|
57
|
+
format: fileFormat
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
destination,
|
|
61
|
+
...(transformations && {
|
|
62
|
+
transformations
|
|
63
|
+
})
|
|
64
|
+
})), {});
|
|
65
|
+
if (!(job != null && job.id)) {
|
|
66
|
+
throw new Error('submitJob returned no job id');
|
|
67
|
+
}
|
|
68
|
+
return job.id;
|
|
69
|
+
},
|
|
70
|
+
async getJob(jobId) {
|
|
71
|
+
const {
|
|
72
|
+
data: {
|
|
73
|
+
job
|
|
74
|
+
}
|
|
75
|
+
} = await errorHandler.withErrorHandler(() => httpClient.request((0, _http.getJob)({
|
|
76
|
+
jobId
|
|
77
|
+
})), {});
|
|
78
|
+
if (!job) {
|
|
79
|
+
throw new Error(`getJob returned no job for "${jobId}"`);
|
|
80
|
+
}
|
|
81
|
+
return job;
|
|
82
|
+
},
|
|
83
|
+
async terminateJob(jobId) {
|
|
84
|
+
await errorHandler.withErrorHandler(() => httpClient.request((0, _http.terminateJob)({
|
|
85
|
+
jobId
|
|
86
|
+
})), {});
|
|
87
|
+
},
|
|
88
|
+
async listMovementLogs(jobId, paging) {
|
|
89
|
+
const {
|
|
90
|
+
data
|
|
91
|
+
} = await errorHandler.withErrorHandler(() => httpClient.request((0, _http.listMovementLogs)({
|
|
92
|
+
jobId,
|
|
93
|
+
...(paging && {
|
|
94
|
+
paging
|
|
95
|
+
})
|
|
96
|
+
})), {});
|
|
97
|
+
return data;
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
//# sourceMappingURL=hubJobClient.js.map
|