@jskit-ai/connectors-core 0.2.4 → 0.2.5
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/locales/en.json +1 -0
- package/package.json +1 -1
- package/src/shared/configuration.js +10 -4
- package/test/connectionService.test.js +40 -0
package/locales/en.json
CHANGED
|
@@ -27,6 +27,7 @@
|
|
|
27
27
|
"schemaVersionUnsupported": "Use numeric schemaVersion 1; other versions require an explicit migration.",
|
|
28
28
|
"scopeRequired": "Select at least one permission.",
|
|
29
29
|
"scopeUnsupported": "A selected permission is not supported by this provider configuration.",
|
|
30
|
+
"secretReference": "Use a reference such as env:VARIABLE_NAME.",
|
|
30
31
|
"serviceAccountPerUser": "A service account cannot connect as each app user.",
|
|
31
32
|
"serviceAccountRegistration": "Service-account credentials do not use a browser OAuth registration.",
|
|
32
33
|
"stableName": "Use a stable lowercase name containing letters, digits and hyphens.",
|
package/package.json
CHANGED
|
@@ -92,17 +92,23 @@ const FIELD_ERRORS = Object.freeze({
|
|
|
92
92
|
clientId: { code: "connectors.configuration.clientId", message: "Check this provider's client identifier." },
|
|
93
93
|
requiredScopes: { code: "connectors.configuration.requiredScopes", message: "Include this provider's required permissions." },
|
|
94
94
|
scopeUnsupported: { code: "connectors.configuration.scopeUnsupported", message: "A selected permission is not supported by this provider configuration." },
|
|
95
|
-
scopeRequired: { code: "connectors.configuration.scopeRequired", message: "Select at least one permission." }
|
|
95
|
+
scopeRequired: { code: "connectors.configuration.scopeRequired", message: "Select at least one permission." },
|
|
96
|
+
secretReference: { code: "connectors.configuration.secretReference", message: "Use a reference such as env:VARIABLE_NAME." }
|
|
96
97
|
});
|
|
97
98
|
|
|
98
|
-
// A schema validation error's field errors with the codes the validator gave them.
|
|
99
|
+
// A schema validation error's field errors with the codes the validator gave them. A
|
|
100
|
+
// credential reference that fails secretReference's pattern says what a reference looks like.
|
|
99
101
|
function schemaFieldErrors(error, fallback, prefix = "") {
|
|
100
102
|
const messages = {};
|
|
101
103
|
const codes = {};
|
|
102
104
|
for (const [field, message] of Object.entries(error.fieldErrors || fallback)) {
|
|
103
|
-
messages[`${prefix}${field}`] = message;
|
|
104
105
|
const coded = error.fieldErrorCodes?.[field];
|
|
105
|
-
if (coded
|
|
106
|
+
if (coded?.code === "kernel.validation.field.pattern" && coded.params.pattern === secretReference.pattern) {
|
|
107
|
+
messages[`${prefix}${field}`] = FIELD_ERRORS.secretReference;
|
|
108
|
+
} else {
|
|
109
|
+
messages[`${prefix}${field}`] = message;
|
|
110
|
+
if (coded) codes[`${prefix}${field}`] = coded;
|
|
111
|
+
}
|
|
106
112
|
}
|
|
107
113
|
return { messages, codes };
|
|
108
114
|
}
|
|
@@ -12,6 +12,8 @@ import { googleCalendarProvider } from "../../connector-google-calendar/src/serv
|
|
|
12
12
|
import { createConnectionService, createConnectorsFeature, createEnvironmentReferenceResolver } from "../src/server/index.js";
|
|
13
13
|
import { integrationsSchema, parseIntegrationConfiguration, validateIntegrationConfiguration } from "../src/shared/configuration.js";
|
|
14
14
|
import { clickhouseProvider } from "../../connectors-catalog/src/server/clickhouse.js";
|
|
15
|
+
import { algoliaDefinition } from "../../connectors-catalog/src/shared/algolia.js";
|
|
16
|
+
import { firebaseCloudMessagingDefinition, firebaseMessagingScope } from "../../connectors-catalog/src/shared/firebase-cloud-messaging.js";
|
|
15
17
|
|
|
16
18
|
const listScope = "https://www.googleapis.com/auth/calendar.calendarlist.readonly";
|
|
17
19
|
const eventsScope = "https://www.googleapis.com/auth/calendar.events.readonly";
|
|
@@ -489,6 +491,44 @@ test("reference fields reject pasted web URLs while preserving environment and c
|
|
|
489
491
|
}
|
|
490
492
|
});
|
|
491
493
|
|
|
494
|
+
function assertReferenceFieldError(input, providers, field) {
|
|
495
|
+
assert.throws(() => validateIntegrationConfiguration(input, { providers }), (error) => {
|
|
496
|
+
assert.equal(error.fieldErrors[field], "Use a reference such as env:VARIABLE_NAME.", field);
|
|
497
|
+
assert.deepEqual(error.fieldErrorCodes[field], { code: "connectors.configuration.secretReference", params: {} }, field);
|
|
498
|
+
return true;
|
|
499
|
+
});
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
test("a secretRef that is not a reference says to use a reference such as env:VARIABLE_NAME", () => {
|
|
503
|
+
for (const value of ["https://x", "plain"]) {
|
|
504
|
+
assertReferenceFieldError(databaseConfiguration({ method: "api-key", secretRef: value }), [clickhouseProvider], "integrations.database.authentication.secretRef");
|
|
505
|
+
}
|
|
506
|
+
});
|
|
507
|
+
|
|
508
|
+
test("a registration clientSecretRef that is not a reference says to use a reference such as env:VARIABLE_NAME", () => {
|
|
509
|
+
for (const value of ["https://x", "plain"]) {
|
|
510
|
+
const input = configuration();
|
|
511
|
+
input.registrations.google.clientSecretRef = value;
|
|
512
|
+
assertReferenceFieldError(input, [googleCalendarProvider], "registrations.google.clientSecretRef");
|
|
513
|
+
}
|
|
514
|
+
});
|
|
515
|
+
|
|
516
|
+
test("a provider setting's reference says the same, while its other pattern failures keep the kernel's sentence", () => {
|
|
517
|
+
const input = { schemaVersion: 1, registrations: {}, integrations: {
|
|
518
|
+
search: { provider: "algolia", accountMode: "shared", scopes: [], authentication: { method: "api-key", secretRef: "env:ALGOLIA_KEY" },
|
|
519
|
+
settings: { applicationId: "APP1", publicApiKeyRef: "raw-search-key" } },
|
|
520
|
+
push: { provider: "firebase-cloud-messaging", accountMode: "shared", scopes: [firebaseMessagingScope], authentication: { method: "service-account", secretRef: "env:FCM" },
|
|
521
|
+
settings: { projectId: "push-project", clientMode: "web", apiKey: "not-a-key" } }
|
|
522
|
+
} };
|
|
523
|
+
const providers = [algoliaDefinition, firebaseCloudMessagingDefinition];
|
|
524
|
+
assertReferenceFieldError(input, providers, "integrations.search.settings.publicApiKeyRef");
|
|
525
|
+
assert.throws(() => validateIntegrationConfiguration(input, { providers }), (error) => {
|
|
526
|
+
assert.equal(error.fieldErrors["integrations.push.settings.apiKey"], "Value does not match the required pattern.");
|
|
527
|
+
assert.equal(error.fieldErrorCodes["integrations.push.settings.apiKey"].code, "kernel.validation.field.pattern");
|
|
528
|
+
return true;
|
|
529
|
+
});
|
|
530
|
+
});
|
|
531
|
+
|
|
492
532
|
test("the exported configuration contract enforces reference constraints for every credential field", async (t) => {
|
|
493
533
|
const app = Fastify({ ajv: { plugins: [(ajv) => ajv.addKeyword("x-json-rest-schema")] } });
|
|
494
534
|
t.after(() => app.close());
|