nucleus-core-ts 0.9.829 → 0.9.831
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/fe/components/IntegrationsPage/components/RunPanel.js +8 -2
- package/dist/fe/components/IntegrationsPage/components/passwordIsNeeded.d.ts +16 -0
- package/dist/fe/components/IntegrationsPage/components/passwordIsNeeded.js +16 -0
- package/dist/fe/components/IntegrationsPage/components/passwordIsNeeded.test.d.ts +1 -0
- package/dist/fe/components/IntegrationsPage/components/passwordIsNeeded.test.js +45 -0
- package/dist/fe/components/IntegrationsPage/components/runFormat.js +7 -0
- package/dist/fe/components/IntegrationsPage/components/runFormat.test.d.ts +1 -0
- package/dist/fe/components/IntegrationsPage/components/runFormat.test.js +47 -0
- package/dist/fe/components/IntegrationsPage/types/records.d.ts +4 -0
- package/package.json +1 -1
|
@@ -5,6 +5,7 @@ import { useIntegrationsStore } from '../store';
|
|
|
5
5
|
import { useLabels } from '../text/context';
|
|
6
6
|
import { integrationsPageTheme } from '../theme';
|
|
7
7
|
import { Notice } from './Primitives';
|
|
8
|
+
import { passwordIsNeeded } from './passwordIsNeeded';
|
|
8
9
|
import { RunConfirmStep } from './RunConfirmStep';
|
|
9
10
|
import { MIN_SHARED_PASSWORD, RunPasswordField } from './RunPasswordField';
|
|
10
11
|
import { RunPlanReferences } from './RunPlanReferences';
|
|
@@ -35,7 +36,12 @@ export function RunPanel({ mapping, actions, onSubscribeProgress, onRunSettled }
|
|
|
35
36
|
// here, once, and goes no further than this run.
|
|
36
37
|
const accountRule = mapping.accountRule ?? null;
|
|
37
38
|
const passwordColumn = mapping.passwordTargetColumn ?? '';
|
|
38
|
-
|
|
39
|
+
// ONE answer, used by the field, the button and the request alike. Asked three
|
|
40
|
+
// times in three places, they drifted: the field appeared and the button
|
|
41
|
+
// enabled, while the request still tested the older condition and sent no
|
|
42
|
+
// password at all — so every import that created logins was refused, by a
|
|
43
|
+
// message the app then replaced with "the import did not start".
|
|
44
|
+
const needsPassword = passwordIsNeeded(mapping);
|
|
39
45
|
const passwordReady = !needsPassword || sharedPassword.length >= MIN_SHARED_PASSWORD;
|
|
40
46
|
const activeRunId = store.activeRunId;
|
|
41
47
|
const plan = store.plan;
|
|
@@ -53,7 +59,7 @@ export function RunPanel({ mapping, actions, onSubscribeProgress, onRunSettled }
|
|
|
53
59
|
const start = (confirm, referenceMode)=>{
|
|
54
60
|
store.setError(null);
|
|
55
61
|
setStarting(true);
|
|
56
|
-
actions.startRun(mapping.id, confirm,
|
|
62
|
+
actions.startRun(mapping.id, confirm, needsPassword ? sharedPassword : undefined, referenceMode).then((result)=>{
|
|
57
63
|
setStarting(false);
|
|
58
64
|
// The server decides what counts as destructive; the panel only asks.
|
|
59
65
|
if (result.needsConfirm) {
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { IntegrationMapping } from '../types';
|
|
2
|
+
/**
|
|
3
|
+
* Does starting this import require a password?
|
|
4
|
+
*
|
|
5
|
+
* Two ways it can: the mapping writes one into a column of the record's own
|
|
6
|
+
* table, or it creates a login alongside each record. Either way the operator
|
|
7
|
+
* types it once, on the run screen, and it goes no further than that run.
|
|
8
|
+
*
|
|
9
|
+
* A function rather than three expressions, because it WAS three: the field that
|
|
10
|
+
* asks for the password, the button that starts the import, and the request that
|
|
11
|
+
* carries it each decided for themselves. Two were updated when accounts were
|
|
12
|
+
* added and one was not — so the field appeared, the button enabled, and the
|
|
13
|
+
* request sent nothing. Every import that created logins was refused for a
|
|
14
|
+
* missing password the operator had typed.
|
|
15
|
+
*/
|
|
16
|
+
export declare function passwordIsNeeded(mapping: IntegrationMapping): boolean;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Does starting this import require a password?
|
|
3
|
+
*
|
|
4
|
+
* Two ways it can: the mapping writes one into a column of the record's own
|
|
5
|
+
* table, or it creates a login alongside each record. Either way the operator
|
|
6
|
+
* types it once, on the run screen, and it goes no further than that run.
|
|
7
|
+
*
|
|
8
|
+
* A function rather than three expressions, because it WAS three: the field that
|
|
9
|
+
* asks for the password, the button that starts the import, and the request that
|
|
10
|
+
* carries it each decided for themselves. Two were updated when accounts were
|
|
11
|
+
* added and one was not — so the field appeared, the button enabled, and the
|
|
12
|
+
* request sent nothing. Every import that created logins was refused for a
|
|
13
|
+
* missing password the operator had typed.
|
|
14
|
+
*/ export function passwordIsNeeded(mapping) {
|
|
15
|
+
return Boolean(mapping.passwordTargetColumn) || mapping.accountRule != null;
|
|
16
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { describe, expect, it } from 'bun:test';
|
|
2
|
+
import { passwordIsNeeded } from './passwordIsNeeded';
|
|
3
|
+
/**
|
|
4
|
+
* The field that ASKS for the password, the button that starts the import and
|
|
5
|
+
* the request that CARRIES it each decided this for themselves. Two were updated
|
|
6
|
+
* when accounts were added and one was not: the field appeared, the button
|
|
7
|
+
* enabled, and the request sent nothing — so every import that created logins
|
|
8
|
+
* was refused for a password the operator had typed.
|
|
9
|
+
*/ const mapping = (over)=>({
|
|
10
|
+
id: 'm1',
|
|
11
|
+
name: 'x',
|
|
12
|
+
targetEntity: 'employees',
|
|
13
|
+
...over
|
|
14
|
+
});
|
|
15
|
+
describe('does this import need a password', ()=>{
|
|
16
|
+
it('yes, when it writes one into the record own table', ()=>{
|
|
17
|
+
expect(passwordIsNeeded(mapping({
|
|
18
|
+
passwordTargetColumn: 'passwordHash'
|
|
19
|
+
}))).toBe(true);
|
|
20
|
+
});
|
|
21
|
+
it('yes, when it creates a login alongside each record', ()=>{
|
|
22
|
+
// The case that was missed. Nothing about the record's own table says so.
|
|
23
|
+
expect(passwordIsNeeded(mapping({
|
|
24
|
+
accountRule: {
|
|
25
|
+
entity: 'users',
|
|
26
|
+
idColumn: 'userId',
|
|
27
|
+
matchColumn: 'email',
|
|
28
|
+
matchFrom: 'email',
|
|
29
|
+
passwordColumn: 'password'
|
|
30
|
+
}
|
|
31
|
+
}))).toBe(true);
|
|
32
|
+
});
|
|
33
|
+
it('no, for an import that creates no accounts at all', ()=>{
|
|
34
|
+
expect(passwordIsNeeded(mapping({}))).toBe(false);
|
|
35
|
+
expect(passwordIsNeeded(mapping({
|
|
36
|
+
passwordTargetColumn: null,
|
|
37
|
+
accountRule: null
|
|
38
|
+
}))).toBe(false);
|
|
39
|
+
});
|
|
40
|
+
it('treats an empty column name as no column', ()=>{
|
|
41
|
+
expect(passwordIsNeeded(mapping({
|
|
42
|
+
passwordTargetColumn: ''
|
|
43
|
+
}))).toBe(false);
|
|
44
|
+
});
|
|
45
|
+
});
|
|
@@ -53,6 +53,13 @@ export function formatDuration(startedAt, finishedAt) {
|
|
|
53
53
|
`${run.updatedCount} updated`,
|
|
54
54
|
`${run.skippedCount} skipped`
|
|
55
55
|
];
|
|
56
|
+
// An import that also opened 877 logins did the largest thing it did that day
|
|
57
|
+
// and said nothing about it — the history read exactly like one that opened
|
|
58
|
+
// none, and the only way to tell was to go and count the users table.
|
|
59
|
+
if ((run.accountsCreatedCount ?? 0) > 0) parts.push(`${run.accountsCreatedCount} login(s)`);
|
|
60
|
+
if ((run.referencesCreatedCount ?? 0) > 0) {
|
|
61
|
+
parts.push(`${run.referencesCreatedCount} value(s) added elsewhere`);
|
|
62
|
+
}
|
|
56
63
|
if (run.deletedCount > 0) parts.push(`${run.deletedCount} deleted`);
|
|
57
64
|
if (run.deactivatedCount > 0) parts.push(`${run.deactivatedCount} deactivated`);
|
|
58
65
|
if (run.errorCount > 0) parts.push(`${run.errorCount} failed`);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { describe, expect, it } from 'bun:test';
|
|
2
|
+
import { countersLine } from './runFormat';
|
|
3
|
+
/**
|
|
4
|
+
* An import that also opened 877 logins did the largest thing it did that day.
|
|
5
|
+
* The history line read exactly like one that opened none, and the only way to
|
|
6
|
+
* tell them apart was to go and count the users table.
|
|
7
|
+
*/ const run = (over)=>({
|
|
8
|
+
createdCount: 0,
|
|
9
|
+
updatedCount: 0,
|
|
10
|
+
skippedCount: 0,
|
|
11
|
+
deletedCount: 0,
|
|
12
|
+
deactivatedCount: 0,
|
|
13
|
+
errorCount: 0,
|
|
14
|
+
...over
|
|
15
|
+
});
|
|
16
|
+
describe('the counts of a finished run', ()=>{
|
|
17
|
+
it('always says what it created, updated and skipped', ()=>{
|
|
18
|
+
expect(countersLine(run({
|
|
19
|
+
createdCount: 3,
|
|
20
|
+
updatedCount: 2,
|
|
21
|
+
skippedCount: 1
|
|
22
|
+
}))).toBe('3 created · 2 updated · 1 skipped');
|
|
23
|
+
});
|
|
24
|
+
it('says how many logins it opened', ()=>{
|
|
25
|
+
expect(countersLine(run({
|
|
26
|
+
createdCount: 880,
|
|
27
|
+
accountsCreatedCount: 877
|
|
28
|
+
}))).toContain('877 login(s)');
|
|
29
|
+
});
|
|
30
|
+
it('says what it added to other tables', ()=>{
|
|
31
|
+
expect(countersLine(run({
|
|
32
|
+
referencesCreatedCount: 2
|
|
33
|
+
}))).toContain('2 value(s) added elsewhere');
|
|
34
|
+
});
|
|
35
|
+
it('stays quiet about the kinds that did not happen', ()=>{
|
|
36
|
+
// A line of zeros is a line nobody reads.
|
|
37
|
+
const line = countersLine(run({
|
|
38
|
+
createdCount: 5
|
|
39
|
+
}));
|
|
40
|
+
expect(line).not.toContain('login');
|
|
41
|
+
expect(line).not.toContain('deleted');
|
|
42
|
+
expect(line).not.toContain('failed');
|
|
43
|
+
});
|
|
44
|
+
it('reads an older run that never recorded these at all', ()=>{
|
|
45
|
+
expect(()=>countersLine(run({}))).not.toThrow();
|
|
46
|
+
});
|
|
47
|
+
});
|
|
@@ -180,6 +180,10 @@ export type IntegrationRun = {
|
|
|
180
180
|
totalFetched: number;
|
|
181
181
|
sourceReportedTotal?: number | null;
|
|
182
182
|
createdCount: number;
|
|
183
|
+
/** Logins this run opened so its records could be signed in to. */
|
|
184
|
+
accountsCreatedCount?: number;
|
|
185
|
+
/** Rows it added to OTHER tables so its records had something to point at. */
|
|
186
|
+
referencesCreatedCount?: number;
|
|
183
187
|
updatedCount: number;
|
|
184
188
|
skippedCount: number;
|
|
185
189
|
deletedCount: number;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nucleus-core-ts",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.831",
|
|
4
4
|
"description": "Production-ready, enterprise-grade TypeScript framework for building multi-tenant APIs",
|
|
5
5
|
"author": "Hidayet Can Özcan <hidayetcan@gmail.com>",
|
|
6
6
|
"license": "SEE LICENSE IN LICENSE",
|