@ankhorage/data-sources 0.6.0 → 1.0.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 +6 -0
- package/README.md +5 -5
- package/dist/discovery/index.d.ts +3 -3
- package/dist/discovery/index.d.ts.map +1 -1
- package/dist/discovery/index.js +19 -28
- package/dist/discovery/index.js.map +1 -1
- package/dist/generated-api/index.d.ts +10 -0
- package/dist/generated-api/index.d.ts.map +1 -0
- package/dist/generated-api/index.js +272 -0
- package/dist/generated-api/index.js.map +1 -0
- package/dist/graphql/index.d.ts +3 -3
- package/dist/graphql/index.d.ts.map +1 -1
- package/dist/graphql/index.js +24 -60
- package/dist/graphql/index.js.map +1 -1
- package/dist/index.d.ts +9 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +17 -4
- package/dist/index.js.map +1 -1
- package/dist/openapi/index.d.ts +2 -2
- package/dist/openapi/index.d.ts.map +1 -1
- package/dist/openapi/index.js +19 -29
- package/dist/openapi/index.js.map +1 -1
- package/dist/rest/index.d.ts +3 -3
- package/dist/rest/index.d.ts.map +1 -1
- package/dist/rest/index.js +51 -51
- package/dist/rest/index.js.map +1 -1
- package/dist/test-runner/index.d.ts.map +1 -1
- package/dist/test-runner/index.js +60 -104
- package/dist/test-runner/index.js.map +1 -1
- package/package.json +2 -2
- package/src/discovery/index.test.ts +25 -101
- package/src/discovery/index.ts +22 -35
- package/src/{managed-api → generated-api}/index.test.ts +59 -67
- package/src/generated-api/index.ts +410 -0
- package/src/graphql/index.test.ts +28 -134
- package/src/graphql/index.ts +29 -89
- package/src/index.test.ts +25 -18
- package/src/index.ts +28 -7
- package/src/openapi/index.test.ts +36 -246
- package/src/openapi/index.ts +22 -60
- package/src/rest/index.test.ts +22 -173
- package/src/rest/index.ts +62 -59
- package/src/test-runner/index.test.ts +82 -150
- package/src/test-runner/index.ts +73 -143
- package/dist/managed-api/index.d.ts +0 -30
- package/dist/managed-api/index.d.ts.map +0 -1
- package/dist/managed-api/index.js +0 -257
- package/dist/managed-api/index.js.map +0 -1
- package/src/managed-api/index.ts +0 -364
|
@@ -1,18 +1,12 @@
|
|
|
1
1
|
export async function testEndpoint(input) {
|
|
2
2
|
const built = await buildEndpointTestRequest(input);
|
|
3
|
-
if (!built.ok)
|
|
4
|
-
return built;
|
|
5
|
-
if (input.dryRun === true)
|
|
3
|
+
if (!built.ok || input.dryRun === true)
|
|
6
4
|
return built;
|
|
7
5
|
if (input.fetch === undefined) {
|
|
8
|
-
return
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
...built.diagnostics,
|
|
13
|
-
createDiagnostic(input, 'invalid-config', 'Endpoint test runner requires an injected fetch function.'),
|
|
14
|
-
],
|
|
15
|
-
};
|
|
6
|
+
return failure(built.request, [
|
|
7
|
+
...built.diagnostics,
|
|
8
|
+
createDiagnostic(input, 'invalid-config', 'Endpoint test runner requires an injected fetch function.'),
|
|
9
|
+
]);
|
|
16
10
|
}
|
|
17
11
|
try {
|
|
18
12
|
const response = await input.fetch(built.request.url, {
|
|
@@ -33,33 +27,26 @@ export async function testEndpoint(input) {
|
|
|
33
27
|
data: responseDiagnostic.parsedBody,
|
|
34
28
|
diagnostics,
|
|
35
29
|
}
|
|
36
|
-
:
|
|
37
|
-
ok: false,
|
|
38
|
-
request: built.request,
|
|
39
|
-
response: responseDiagnostic,
|
|
40
|
-
diagnostics,
|
|
41
|
-
};
|
|
30
|
+
: failure(built.request, diagnostics, responseDiagnostic);
|
|
42
31
|
}
|
|
43
32
|
catch (error) {
|
|
44
|
-
return
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
...built.diagnostics,
|
|
49
|
-
createDiagnostic(input, 'network-error', createNetworkErrorMessage(error)),
|
|
50
|
-
],
|
|
51
|
-
};
|
|
33
|
+
return failure(built.request, [
|
|
34
|
+
...built.diagnostics,
|
|
35
|
+
createDiagnostic(input, 'network-error', createNetworkErrorMessage(error)),
|
|
36
|
+
]);
|
|
52
37
|
}
|
|
53
38
|
}
|
|
54
39
|
export async function buildEndpointTestRequest(input) {
|
|
55
40
|
const selection = selectEndpointOperation(input);
|
|
56
41
|
if (selection === undefined) {
|
|
57
|
-
return
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
42
|
+
return failure(undefined, [
|
|
43
|
+
createDiagnostic(input, 'missing-operation', 'Endpoint or operation could not be found.'),
|
|
44
|
+
]);
|
|
45
|
+
}
|
|
46
|
+
if (selection.operation.protocol === 'database') {
|
|
47
|
+
return failure(undefined, [
|
|
48
|
+
createDiagnostic(input, 'invalid-config', 'Database-backed generated API operations require an adapter executor and cannot be tested through the HTTP endpoint runner.'),
|
|
49
|
+
]);
|
|
63
50
|
}
|
|
64
51
|
const diagnostics = [];
|
|
65
52
|
const credential = await resolveEndpointCredential(input, selection, diagnostics);
|
|
@@ -67,43 +54,33 @@ export async function buildEndpointTestRequest(input) {
|
|
|
67
54
|
? buildGraphQlRequest(input, selection, credential, diagnostics)
|
|
68
55
|
: buildHttpRequest(input, selection, credential, diagnostics);
|
|
69
56
|
if (request === undefined || diagnostics.some((diagnostic) => diagnostic.severity === 'error')) {
|
|
70
|
-
return
|
|
71
|
-
ok: false,
|
|
72
|
-
request,
|
|
73
|
-
diagnostics,
|
|
74
|
-
};
|
|
57
|
+
return failure(request, diagnostics);
|
|
75
58
|
}
|
|
76
|
-
return {
|
|
77
|
-
ok: true,
|
|
78
|
-
request,
|
|
79
|
-
diagnostics,
|
|
80
|
-
};
|
|
59
|
+
return { ok: true, request, diagnostics };
|
|
81
60
|
}
|
|
82
61
|
function selectEndpointOperation(input) {
|
|
83
62
|
const endpoint = input.dataSource.endpoints[input.endpointId];
|
|
84
63
|
const operation = endpoint?.operations[input.operationId];
|
|
85
|
-
|
|
86
|
-
return undefined;
|
|
87
|
-
return { endpoint, operation };
|
|
64
|
+
return endpoint === undefined || operation === undefined ? undefined : { endpoint, operation };
|
|
88
65
|
}
|
|
89
66
|
async function resolveEndpointCredential(input, selection, diagnostics) {
|
|
90
|
-
const
|
|
91
|
-
if (
|
|
67
|
+
const ref = selection.operation.credential ?? selection.endpoint.credential ?? input.dataSource.credential;
|
|
68
|
+
if (ref === undefined)
|
|
92
69
|
return undefined;
|
|
93
70
|
if (input.credentialResolver === undefined) {
|
|
94
|
-
diagnostics.push(createDiagnostic(input, 'missing-credential', `Credential '${
|
|
71
|
+
diagnostics.push(createDiagnostic(input, 'missing-credential', `Credential '${ref.id}' requires a resolver.`));
|
|
95
72
|
return undefined;
|
|
96
73
|
}
|
|
97
|
-
const credential = await input.credentialResolver(
|
|
74
|
+
const credential = await input.credentialResolver(ref);
|
|
98
75
|
if (credential === undefined) {
|
|
99
|
-
diagnostics.push(createDiagnostic(input, 'missing-credential', `Credential '${
|
|
76
|
+
diagnostics.push(createDiagnostic(input, 'missing-credential', `Credential '${ref.id}' could not be resolved.`));
|
|
100
77
|
}
|
|
101
78
|
return credential;
|
|
102
79
|
}
|
|
103
80
|
function buildHttpRequest(input, selection, credential, diagnostics) {
|
|
104
81
|
const baseUrl = getDataSourceBaseUrl(input.dataSource, selection.endpoint);
|
|
105
82
|
const path = selection.operation.path ?? selection.endpoint.path;
|
|
106
|
-
if (baseUrl
|
|
83
|
+
if (!baseUrl) {
|
|
107
84
|
diagnostics.push(createDiagnostic(input, 'invalid-config', 'HTTP endpoint requires a base URL.'));
|
|
108
85
|
return undefined;
|
|
109
86
|
}
|
|
@@ -113,21 +90,17 @@ function buildHttpRequest(input, selection, credential, diagnostics) {
|
|
|
113
90
|
}
|
|
114
91
|
const values = input.values ?? {};
|
|
115
92
|
const parameters = selection.operation.request?.parameters ?? [];
|
|
116
|
-
const
|
|
93
|
+
const resolvedPath = interpolatePath(path, parameters, values, input, diagnostics);
|
|
117
94
|
const query = collectQuery(parameters, values, credential?.query);
|
|
118
|
-
const url = appendQuery(joinUrl(baseUrl, pathResult), query);
|
|
119
95
|
const body = createHttpBody(selection.operation, values);
|
|
120
|
-
const headers = {
|
|
121
|
-
|
|
122
|
-
};
|
|
123
|
-
if (body !== undefined) {
|
|
96
|
+
const headers = { ...(credential?.headers ?? {}) };
|
|
97
|
+
if (body !== undefined)
|
|
124
98
|
headers['content-type'] = headers['content-type'] ?? 'application/json';
|
|
125
|
-
}
|
|
126
99
|
return {
|
|
127
100
|
dataSourceId: input.dataSource.id,
|
|
128
101
|
endpointId: input.endpointId,
|
|
129
102
|
operationId: input.operationId,
|
|
130
|
-
url,
|
|
103
|
+
url: appendQuery(joinUrl(baseUrl, resolvedPath), query),
|
|
131
104
|
method: selection.operation.method ?? 'GET',
|
|
132
105
|
headers,
|
|
133
106
|
body,
|
|
@@ -136,7 +109,7 @@ function buildHttpRequest(input, selection, credential, diagnostics) {
|
|
|
136
109
|
}
|
|
137
110
|
function buildGraphQlRequest(input, selection, credential, diagnostics) {
|
|
138
111
|
const endpointUrl = getGraphQlEndpointUrl(input.dataSource, selection.endpoint);
|
|
139
|
-
if (endpointUrl
|
|
112
|
+
if (!endpointUrl) {
|
|
140
113
|
diagnostics.push(createDiagnostic(input, 'invalid-config', 'GraphQL endpoint requires an endpoint URL.'));
|
|
141
114
|
return undefined;
|
|
142
115
|
}
|
|
@@ -147,52 +120,45 @@ function buildGraphQlRequest(input, selection, credential, diagnostics) {
|
|
|
147
120
|
return undefined;
|
|
148
121
|
}
|
|
149
122
|
const variables = getRecordValue(input.values, 'variables');
|
|
150
|
-
const body = JSON.stringify({ query, variables: variables ?? {} });
|
|
151
|
-
const headers = {
|
|
152
|
-
...(credential?.headers ?? {}),
|
|
153
|
-
'content-type': 'application/json',
|
|
154
|
-
};
|
|
155
123
|
return {
|
|
156
124
|
dataSourceId: input.dataSource.id,
|
|
157
125
|
endpointId: input.endpointId,
|
|
158
126
|
operationId: input.operationId,
|
|
159
127
|
url: appendQuery(endpointUrl, credential?.query ?? {}),
|
|
160
128
|
method: 'POST',
|
|
161
|
-
headers,
|
|
162
|
-
body,
|
|
129
|
+
headers: { ...(credential?.headers ?? {}), 'content-type': 'application/json' },
|
|
130
|
+
body: JSON.stringify({ query, variables: variables ?? {} }),
|
|
163
131
|
dryRun: input.dryRun === true,
|
|
164
132
|
};
|
|
165
133
|
}
|
|
166
|
-
function getDataSourceBaseUrl(
|
|
134
|
+
function getDataSourceBaseUrl(source, endpoint) {
|
|
167
135
|
if (endpoint.baseUrl !== undefined)
|
|
168
136
|
return endpoint.baseUrl;
|
|
169
|
-
if (
|
|
170
|
-
return
|
|
171
|
-
|
|
172
|
-
return dataSource.endpointUrl;
|
|
173
|
-
return undefined;
|
|
137
|
+
if (source.kind !== 'api' || source.origin !== 'external')
|
|
138
|
+
return undefined;
|
|
139
|
+
return source.protocol === 'rest' ? source.baseUrl : source.endpointUrl;
|
|
174
140
|
}
|
|
175
|
-
function getGraphQlEndpointUrl(
|
|
141
|
+
function getGraphQlEndpointUrl(source, endpoint) {
|
|
176
142
|
if (endpoint.baseUrl !== undefined)
|
|
177
143
|
return endpoint.baseUrl;
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
144
|
+
return source.kind === 'api' && source.origin === 'external' && source.protocol === 'graphql'
|
|
145
|
+
? source.endpointUrl
|
|
146
|
+
: undefined;
|
|
181
147
|
}
|
|
182
148
|
function interpolatePath(path, parameters, values, input, diagnostics) {
|
|
183
|
-
let
|
|
184
|
-
const
|
|
185
|
-
for (const parameter of pathParameters) {
|
|
149
|
+
let next = path;
|
|
150
|
+
for (const parameter of parameters.filter((item) => item.location === 'path')) {
|
|
186
151
|
const value = values[parameter.name] ?? parameter.default;
|
|
187
152
|
if (value === undefined) {
|
|
188
153
|
diagnostics.push(createDiagnostic(input, 'invalid-config', `Missing required path parameter '${parameter.name}'.`));
|
|
189
154
|
continue;
|
|
190
155
|
}
|
|
191
|
-
const
|
|
192
|
-
|
|
193
|
-
|
|
156
|
+
const encoded = encodeURIComponent(serializeEndpointUrlValue(value));
|
|
157
|
+
next = next
|
|
158
|
+
.replaceAll(`{${parameter.name}}`, encoded)
|
|
159
|
+
.replaceAll(`:${parameter.name}`, encoded);
|
|
194
160
|
}
|
|
195
|
-
return
|
|
161
|
+
return next;
|
|
196
162
|
}
|
|
197
163
|
function collectQuery(parameters, values, credentialQuery) {
|
|
198
164
|
const query = { ...(credentialQuery ?? {}) };
|
|
@@ -208,20 +174,16 @@ function collectQuery(parameters, values, credentialQuery) {
|
|
|
208
174
|
function createHttpBody(operation, values) {
|
|
209
175
|
if (operation.method === 'GET' || operation.method === 'HEAD')
|
|
210
176
|
return undefined;
|
|
211
|
-
|
|
212
|
-
if (body === undefined)
|
|
213
|
-
return undefined;
|
|
214
|
-
return JSON.stringify(body);
|
|
177
|
+
return values.body === undefined ? undefined : JSON.stringify(values.body);
|
|
215
178
|
}
|
|
216
179
|
async function createResponseDiagnostic(response) {
|
|
217
180
|
const bodyText = await response.text();
|
|
218
|
-
const parsedBody = parseResponseBody(bodyText);
|
|
219
181
|
return {
|
|
220
182
|
status: response.status,
|
|
221
183
|
ok: response.status >= 200 && response.status < 300,
|
|
222
184
|
headers: response.headers,
|
|
223
185
|
bodyText,
|
|
224
|
-
parsedBody,
|
|
186
|
+
parsedBody: parseResponseBody(bodyText),
|
|
225
187
|
};
|
|
226
188
|
}
|
|
227
189
|
function parseResponseBody(bodyText) {
|
|
@@ -242,8 +204,7 @@ function appendQuery(url, query) {
|
|
|
242
204
|
const search = entries
|
|
243
205
|
.map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(serializeEndpointUrlValue(value))}`)
|
|
244
206
|
.join('&');
|
|
245
|
-
|
|
246
|
-
return `${url}${separator}${search}`;
|
|
207
|
+
return `${url}${url.includes('?') ? '&' : '?'}${search}`;
|
|
247
208
|
}
|
|
248
209
|
function serializeEndpointUrlValue(value) {
|
|
249
210
|
if (typeof value === 'string')
|
|
@@ -255,9 +216,9 @@ function serializeEndpointUrlValue(value) {
|
|
|
255
216
|
return JSON.stringify(value);
|
|
256
217
|
}
|
|
257
218
|
function joinUrl(baseUrl, path) {
|
|
258
|
-
const
|
|
219
|
+
const normalizedBaseUrl = baseUrl.endsWith('/') ? baseUrl.slice(0, -1) : baseUrl;
|
|
259
220
|
const normalizedPath = path.startsWith('/') ? path : `/${path}`;
|
|
260
|
-
return `${
|
|
221
|
+
return `${normalizedBaseUrl}${normalizedPath}`;
|
|
261
222
|
}
|
|
262
223
|
function getStringValue(values, key) {
|
|
263
224
|
const value = values?.[key];
|
|
@@ -283,23 +244,18 @@ function createDiagnostic(input, code, message) {
|
|
|
283
244
|
severity: code === 'missing-schema' ? 'info' : 'error',
|
|
284
245
|
};
|
|
285
246
|
}
|
|
247
|
+
function failure(request, diagnostics, response) {
|
|
248
|
+
return { ok: false, request, response, diagnostics };
|
|
249
|
+
}
|
|
286
250
|
function createNetworkErrorMessage(error) {
|
|
287
|
-
|
|
288
|
-
return error.message;
|
|
289
|
-
return 'Endpoint test request failed.';
|
|
251
|
+
return error instanceof Error ? error.message : 'Endpoint test request failed.';
|
|
290
252
|
}
|
|
291
253
|
function isDataContractValue(value) {
|
|
292
|
-
if (value === null ||
|
|
293
|
-
typeof value === 'string' ||
|
|
294
|
-
typeof value === 'number' ||
|
|
295
|
-
typeof value === 'boolean') {
|
|
254
|
+
if (value === null || ['string', 'number', 'boolean'].includes(typeof value))
|
|
296
255
|
return true;
|
|
297
|
-
}
|
|
298
256
|
if (Array.isArray(value))
|
|
299
257
|
return value.every(isDataContractValue);
|
|
300
|
-
|
|
301
|
-
return false;
|
|
302
|
-
return Object.values(value).every(isDataContractValue);
|
|
258
|
+
return typeof value === 'object' && Object.values(value).every(isDataContractValue);
|
|
303
259
|
}
|
|
304
260
|
function isDataContractRecord(value) {
|
|
305
261
|
return (value !== undefined && typeof value === 'object' && value !== null && !Array.isArray(value));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/test-runner/index.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n CredentialRef,\n DataContractValue,\n DataEndpointConfig,\n DataOperationConfig,\n DataOperationParameter,\n DataSourceConfig,\n DataSourceDiagnostic,\n EndpointId,\n OperationId,\n} from '@ankhorage/contracts/data';\n\nexport type EndpointTestInputValues = Readonly<Record<string, DataContractValue>>;\nexport type EndpointTestHeaders = Readonly<Record<string, string>>;\n\nexport interface EndpointTestCredential {\n readonly headers?: EndpointTestHeaders;\n readonly query?: EndpointTestInputValues;\n}\n\nexport type EndpointTestCredentialResolver = (\n credential: CredentialRef,\n) => EndpointTestCredential | Promise<EndpointTestCredential | undefined> | undefined;\n\nexport interface EndpointTestFetchInit {\n readonly method: string;\n readonly headers: EndpointTestHeaders;\n readonly body?: string;\n}\n\nexport interface EndpointTestFetchResponse {\n readonly status: number;\n readonly headers?: EndpointTestHeaders;\n text(): Promise<string>;\n}\n\nexport type EndpointTestFetch = (\n url: string,\n init: EndpointTestFetchInit,\n) => Promise<EndpointTestFetchResponse>;\n\nexport interface EndpointTestInput {\n readonly dataSource: DataSourceConfig;\n readonly endpointId: EndpointId;\n readonly operationId: OperationId;\n readonly values?: EndpointTestInputValues;\n readonly dryRun?: boolean;\n readonly fetch?: EndpointTestFetch;\n readonly credentialResolver?: EndpointTestCredentialResolver;\n}\n\nexport interface EndpointTestRequestDiagnostic {\n readonly dataSourceId: string;\n readonly endpointId: EndpointId;\n readonly operationId: OperationId;\n readonly url: string;\n readonly method: string;\n readonly headers: EndpointTestHeaders;\n readonly body?: string;\n readonly dryRun: boolean;\n}\n\nexport interface EndpointTestResponseDiagnostic {\n readonly status: number;\n readonly ok: boolean;\n readonly headers?: EndpointTestHeaders;\n readonly bodyText?: string;\n readonly parsedBody?: DataContractValue;\n}\n\nexport type EndpointTestResult =\n | {\n readonly ok: true;\n readonly request: EndpointTestRequestDiagnostic;\n readonly response?: EndpointTestResponseDiagnostic;\n readonly data?: DataContractValue;\n readonly diagnostics: readonly DataSourceDiagnostic[];\n }\n | {\n readonly ok: false;\n readonly request?: EndpointTestRequestDiagnostic;\n readonly response?: EndpointTestResponseDiagnostic;\n readonly diagnostics: readonly DataSourceDiagnostic[];\n };\n\ninterface EndpointOperationSelection {\n readonly endpoint: DataEndpointConfig;\n readonly operation: DataOperationConfig;\n}\n\nexport async function testEndpoint(input: EndpointTestInput): Promise<EndpointTestResult> {\n const built = await buildEndpointTestRequest(input);\n\n if (!built.ok) return built;\n if (input.dryRun === true) return built;\n\n if (input.fetch === undefined) {\n return {\n ok: false,\n request: built.request,\n diagnostics: [\n ...built.diagnostics,\n createDiagnostic(\n input,\n 'invalid-config',\n 'Endpoint test runner requires an injected fetch function.',\n ),\n ],\n };\n }\n\n try {\n const response = await input.fetch(built.request.url, {\n method: built.request.method,\n headers: built.request.headers,\n body: built.request.body,\n });\n const responseDiagnostic = await createResponseDiagnostic(response);\n const diagnostics = [...built.diagnostics];\n\n if (!responseDiagnostic.ok) {\n diagnostics.push(\n createDiagnostic(\n input,\n 'http-error',\n `Endpoint test request returned HTTP status ${responseDiagnostic.status}.`,\n ),\n );\n }\n\n return responseDiagnostic.ok\n ? {\n ok: true,\n request: built.request,\n response: responseDiagnostic,\n data: responseDiagnostic.parsedBody,\n diagnostics,\n }\n : {\n ok: false,\n request: built.request,\n response: responseDiagnostic,\n diagnostics,\n };\n } catch (error) {\n return {\n ok: false,\n request: built.request,\n diagnostics: [\n ...built.diagnostics,\n createDiagnostic(input, 'network-error', createNetworkErrorMessage(error)),\n ],\n };\n }\n}\n\nexport async function buildEndpointTestRequest(\n input: EndpointTestInput,\n): Promise<EndpointTestResult> {\n const selection = selectEndpointOperation(input);\n if (selection === undefined) {\n return {\n ok: false,\n diagnostics: [\n createDiagnostic(input, 'missing-operation', 'Endpoint or operation could not be found.'),\n ],\n };\n }\n\n const diagnostics: DataSourceDiagnostic[] = [];\n const credential = await resolveEndpointCredential(input, selection, diagnostics);\n const request =\n selection.operation.protocol === 'graphql'\n ? buildGraphQlRequest(input, selection, credential, diagnostics)\n : buildHttpRequest(input, selection, credential, diagnostics);\n\n if (request === undefined || diagnostics.some((diagnostic) => diagnostic.severity === 'error')) {\n return {\n ok: false,\n request,\n diagnostics,\n };\n }\n\n return {\n ok: true,\n request,\n diagnostics,\n };\n}\n\nfunction selectEndpointOperation(input: EndpointTestInput): EndpointOperationSelection | undefined {\n const endpoint = input.dataSource.endpoints[input.endpointId];\n const operation = endpoint?.operations[input.operationId];\n\n if (endpoint === undefined || operation === undefined) return undefined;\n\n return { endpoint, operation };\n}\n\nasync function resolveEndpointCredential(\n input: EndpointTestInput,\n selection: EndpointOperationSelection,\n diagnostics: DataSourceDiagnostic[],\n): Promise<EndpointTestCredential | undefined> {\n const credentialRef =\n selection.operation.credential ?? selection.endpoint.credential ?? input.dataSource.credential;\n\n if (credentialRef === undefined) return undefined;\n\n if (input.credentialResolver === undefined) {\n diagnostics.push(\n createDiagnostic(\n input,\n 'missing-credential',\n `Credential '${credentialRef.id}' requires a resolver.`,\n ),\n );\n return undefined;\n }\n\n const credential = await input.credentialResolver(credentialRef);\n\n if (credential === undefined) {\n diagnostics.push(\n createDiagnostic(\n input,\n 'missing-credential',\n `Credential '${credentialRef.id}' could not be resolved.`,\n ),\n );\n }\n\n return credential;\n}\n\nfunction buildHttpRequest(\n input: EndpointTestInput,\n selection: EndpointOperationSelection,\n credential: EndpointTestCredential | undefined,\n diagnostics: DataSourceDiagnostic[],\n): EndpointTestRequestDiagnostic | undefined {\n const baseUrl = getDataSourceBaseUrl(input.dataSource, selection.endpoint);\n const path = selection.operation.path ?? selection.endpoint.path;\n\n if (baseUrl === undefined || baseUrl.trim().length === 0) {\n diagnostics.push(\n createDiagnostic(input, 'invalid-config', 'HTTP endpoint requires a base URL.'),\n );\n return undefined;\n }\n\n if (path === undefined) {\n diagnostics.push(createDiagnostic(input, 'invalid-config', 'HTTP operation requires a path.'));\n return undefined;\n }\n\n const values = input.values ?? {};\n const parameters = selection.operation.request?.parameters ?? [];\n const pathResult = interpolatePath(path, parameters, values, input, diagnostics);\n const query = collectQuery(parameters, values, credential?.query);\n const url = appendQuery(joinUrl(baseUrl, pathResult), query);\n const body = createHttpBody(selection.operation, values);\n const headers: Record<string, string> = {\n ...(credential?.headers ?? {}),\n };\n\n if (body !== undefined) {\n headers['content-type'] = headers['content-type'] ?? 'application/json';\n }\n\n return {\n dataSourceId: input.dataSource.id,\n endpointId: input.endpointId,\n operationId: input.operationId,\n url,\n method: selection.operation.method ?? 'GET',\n headers,\n body,\n dryRun: input.dryRun === true,\n };\n}\n\nfunction buildGraphQlRequest(\n input: EndpointTestInput,\n selection: EndpointOperationSelection,\n credential: EndpointTestCredential | undefined,\n diagnostics: DataSourceDiagnostic[],\n): EndpointTestRequestDiagnostic | undefined {\n const endpointUrl = getGraphQlEndpointUrl(input.dataSource, selection.endpoint);\n\n if (endpointUrl === undefined || endpointUrl.trim().length === 0) {\n diagnostics.push(\n createDiagnostic(input, 'invalid-config', 'GraphQL endpoint requires an endpoint URL.'),\n );\n return undefined;\n }\n\n const query =\n getStringMetadataValue(selection.operation.metadata, 'document') ??\n getStringValue(input.values, 'query');\n\n if (query === undefined) {\n diagnostics.push(\n createDiagnostic(input, 'invalid-config', 'GraphQL operation requires a query document.'),\n );\n return undefined;\n }\n\n const variables = getRecordValue(input.values, 'variables');\n const body = JSON.stringify({ query, variables: variables ?? {} });\n const headers: Record<string, string> = {\n ...(credential?.headers ?? {}),\n 'content-type': 'application/json',\n };\n\n return {\n dataSourceId: input.dataSource.id,\n endpointId: input.endpointId,\n operationId: input.operationId,\n url: appendQuery(endpointUrl, credential?.query ?? {}),\n method: 'POST',\n headers,\n body,\n dryRun: input.dryRun === true,\n };\n}\n\nfunction getDataSourceBaseUrl(\n dataSource: DataSourceConfig,\n endpoint: DataEndpointConfig,\n): string | undefined {\n if (endpoint.baseUrl !== undefined) return endpoint.baseUrl;\n if (dataSource.kind === 'rest' || dataSource.kind === 'openapi') return dataSource.baseUrl;\n if (dataSource.kind === 'graphql') return dataSource.endpointUrl;\n return undefined;\n}\n\nfunction getGraphQlEndpointUrl(\n dataSource: DataSourceConfig,\n endpoint: DataEndpointConfig,\n): string | undefined {\n if (endpoint.baseUrl !== undefined) return endpoint.baseUrl;\n if (dataSource.kind === 'graphql') return dataSource.endpointUrl;\n return undefined;\n}\n\nfunction interpolatePath(\n path: string,\n parameters: readonly DataOperationParameter[],\n values: EndpointTestInputValues,\n input: EndpointTestInput,\n diagnostics: DataSourceDiagnostic[],\n): string {\n let nextPath = path;\n const pathParameters = parameters.filter((parameter) => parameter.location === 'path');\n\n for (const parameter of pathParameters) {\n const value = values[parameter.name] ?? parameter.default;\n\n if (value === undefined) {\n diagnostics.push(\n createDiagnostic(\n input,\n 'invalid-config',\n `Missing required path parameter '${parameter.name}'.`,\n ),\n );\n continue;\n }\n\n const encodedValue = encodeURIComponent(serializeEndpointUrlValue(value));\n nextPath = nextPath.replaceAll(`{${parameter.name}}`, encodedValue);\n nextPath = nextPath.replaceAll(`:${parameter.name}`, encodedValue);\n }\n\n return nextPath;\n}\n\nfunction collectQuery(\n parameters: readonly DataOperationParameter[],\n values: EndpointTestInputValues,\n credentialQuery: EndpointTestInputValues | undefined,\n): EndpointTestInputValues {\n const query: Record<string, DataContractValue> = { ...(credentialQuery ?? {}) };\n\n for (const parameter of parameters) {\n if (parameter.location !== 'query') continue;\n\n const value = values[parameter.name] ?? parameter.default;\n if (value !== undefined) query[parameter.name] = value;\n }\n\n return query;\n}\n\nfunction createHttpBody(\n operation: DataOperationConfig,\n values: EndpointTestInputValues,\n): string | undefined {\n if (operation.method === 'GET' || operation.method === 'HEAD') return undefined;\n\n const { body } = values;\n if (body === undefined) return undefined;\n\n return JSON.stringify(body);\n}\n\nasync function createResponseDiagnostic(\n response: EndpointTestFetchResponse,\n): Promise<EndpointTestResponseDiagnostic> {\n const bodyText = await response.text();\n const parsedBody = parseResponseBody(bodyText);\n\n return {\n status: response.status,\n ok: response.status >= 200 && response.status < 300,\n headers: response.headers,\n bodyText,\n parsedBody,\n };\n}\n\nfunction parseResponseBody(bodyText: string): DataContractValue | undefined {\n if (bodyText.trim().length === 0) return undefined;\n\n try {\n const parsed: unknown = JSON.parse(bodyText);\n return isDataContractValue(parsed) ? parsed : { raw: bodyText };\n } catch {\n return { raw: bodyText };\n }\n}\n\nfunction appendQuery(url: string, query: EndpointTestInputValues): string {\n const entries = Object.entries(query);\n if (entries.length === 0) return url;\n\n const search = entries\n .map(\n ([key, value]) =>\n `${encodeURIComponent(key)}=${encodeURIComponent(serializeEndpointUrlValue(value))}`,\n )\n .join('&');\n const separator = url.includes('?') ? '&' : '?';\n\n return `${url}${separator}${search}`;\n}\n\nfunction serializeEndpointUrlValue(value: DataContractValue): string {\n if (typeof value === 'string') return value;\n if (typeof value === 'number' || typeof value === 'boolean') return value.toString();\n if (value === null) return '';\n\n return JSON.stringify(value);\n}\n\nfunction joinUrl(baseUrl: string, path: string): string {\n const normalizedBase = baseUrl.endsWith('/') ? baseUrl.slice(0, -1) : baseUrl;\n const normalizedPath = path.startsWith('/') ? path : `/${path}`;\n\n return `${normalizedBase}${normalizedPath}`;\n}\n\nfunction getStringValue(\n values: EndpointTestInputValues | undefined,\n key: string,\n): string | undefined {\n const value = values?.[key];\n return typeof value === 'string' ? value : undefined;\n}\n\nfunction getRecordValue(\n values: EndpointTestInputValues | undefined,\n key: string,\n): DataContractValue | undefined {\n const value = values?.[key];\n return isDataContractValue(value) ? value : undefined;\n}\n\nfunction getStringMetadataValue(\n metadata: DataContractValue | undefined,\n key: string,\n): string | undefined {\n if (!isDataContractRecord(metadata)) return undefined;\n\n const value = metadata[key];\n return typeof value === 'string' ? value : undefined;\n}\n\nfunction createDiagnostic(\n input: EndpointTestInput,\n code: string,\n message: string,\n): DataSourceDiagnostic {\n return {\n code,\n dataSourceId: input.dataSource.id,\n endpointId: input.endpointId,\n operationId: input.operationId,\n message,\n severity: code === 'missing-schema' ? 'info' : 'error',\n };\n}\n\nfunction createNetworkErrorMessage(error: unknown): string {\n if (error instanceof Error) return error.message;\n return 'Endpoint test request failed.';\n}\n\nfunction isDataContractValue(value: unknown): value is DataContractValue {\n if (\n value === null ||\n typeof value === 'string' ||\n typeof value === 'number' ||\n typeof value === 'boolean'\n ) {\n return true;\n }\n\n if (Array.isArray(value)) return value.every(isDataContractValue);\n if (typeof value !== 'object') return false;\n\n return Object.values(value).every(isDataContractValue);\n}\n\nfunction isDataContractRecord(\n value: DataContractValue | undefined,\n): value is Record<string, DataContractValue> {\n return (\n value !== undefined && typeof value === 'object' && value !== null && !Array.isArray(value)\n );\n}\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/test-runner/index.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n CredentialRef,\n DataContractValue,\n DataEndpointConfig,\n DataOperationConfig,\n DataOperationParameter,\n DataSourceConfig,\n DataSourceDiagnostic,\n EndpointId,\n OperationId,\n} from '@ankhorage/contracts/data';\n\nexport type EndpointTestInputValues = Readonly<Record<string, DataContractValue>>;\nexport type EndpointTestHeaders = Readonly<Record<string, string>>;\n\nexport interface EndpointTestCredential {\n readonly headers?: EndpointTestHeaders;\n readonly query?: EndpointTestInputValues;\n}\n\nexport type EndpointTestCredentialResolver = (\n credential: CredentialRef,\n) => EndpointTestCredential | Promise<EndpointTestCredential | undefined> | undefined;\n\nexport interface EndpointTestFetchInit {\n readonly method: string;\n readonly headers: EndpointTestHeaders;\n readonly body?: string;\n}\n\nexport interface EndpointTestFetchResponse {\n readonly status: number;\n readonly headers?: EndpointTestHeaders;\n text(): Promise<string>;\n}\n\nexport type EndpointTestFetch = (\n url: string,\n init: EndpointTestFetchInit,\n) => Promise<EndpointTestFetchResponse>;\n\nexport interface EndpointTestInput {\n readonly dataSource: DataSourceConfig;\n readonly endpointId: EndpointId;\n readonly operationId: OperationId;\n readonly values?: EndpointTestInputValues;\n readonly dryRun?: boolean;\n readonly fetch?: EndpointTestFetch;\n readonly credentialResolver?: EndpointTestCredentialResolver;\n}\n\nexport interface EndpointTestRequestDiagnostic {\n readonly dataSourceId: string;\n readonly endpointId: EndpointId;\n readonly operationId: OperationId;\n readonly url: string;\n readonly method: string;\n readonly headers: EndpointTestHeaders;\n readonly body?: string;\n readonly dryRun: boolean;\n}\n\nexport interface EndpointTestResponseDiagnostic {\n readonly status: number;\n readonly ok: boolean;\n readonly headers?: EndpointTestHeaders;\n readonly bodyText?: string;\n readonly parsedBody?: DataContractValue;\n}\n\nexport type EndpointTestResult =\n | {\n readonly ok: true;\n readonly request: EndpointTestRequestDiagnostic;\n readonly response?: EndpointTestResponseDiagnostic;\n readonly data?: DataContractValue;\n readonly diagnostics: readonly DataSourceDiagnostic[];\n }\n | {\n readonly ok: false;\n readonly request?: EndpointTestRequestDiagnostic;\n readonly response?: EndpointTestResponseDiagnostic;\n readonly diagnostics: readonly DataSourceDiagnostic[];\n };\n\ninterface EndpointOperationSelection {\n readonly endpoint: DataEndpointConfig;\n readonly operation: DataOperationConfig;\n}\n\nexport async function testEndpoint(input: EndpointTestInput): Promise<EndpointTestResult> {\n const built = await buildEndpointTestRequest(input);\n if (!built.ok || input.dryRun === true) return built;\n\n if (input.fetch === undefined) {\n return failure(built.request, [\n ...built.diagnostics,\n createDiagnostic(\n input,\n 'invalid-config',\n 'Endpoint test runner requires an injected fetch function.',\n ),\n ]);\n }\n\n try {\n const response = await input.fetch(built.request.url, {\n method: built.request.method,\n headers: built.request.headers,\n body: built.request.body,\n });\n const responseDiagnostic = await createResponseDiagnostic(response);\n const diagnostics = [...built.diagnostics];\n if (!responseDiagnostic.ok) {\n diagnostics.push(\n createDiagnostic(\n input,\n 'http-error',\n `Endpoint test request returned HTTP status ${responseDiagnostic.status}.`,\n ),\n );\n }\n return responseDiagnostic.ok\n ? {\n ok: true,\n request: built.request,\n response: responseDiagnostic,\n data: responseDiagnostic.parsedBody,\n diagnostics,\n }\n : failure(built.request, diagnostics, responseDiagnostic);\n } catch (error) {\n return failure(built.request, [\n ...built.diagnostics,\n createDiagnostic(input, 'network-error', createNetworkErrorMessage(error)),\n ]);\n }\n}\n\nexport async function buildEndpointTestRequest(\n input: EndpointTestInput,\n): Promise<EndpointTestResult> {\n const selection = selectEndpointOperation(input);\n if (selection === undefined) {\n return failure(undefined, [\n createDiagnostic(input, 'missing-operation', 'Endpoint or operation could not be found.'),\n ]);\n }\n\n if (selection.operation.protocol === 'database') {\n return failure(undefined, [\n createDiagnostic(\n input,\n 'invalid-config',\n 'Database-backed generated API operations require an adapter executor and cannot be tested through the HTTP endpoint runner.',\n ),\n ]);\n }\n\n const diagnostics: DataSourceDiagnostic[] = [];\n const credential = await resolveEndpointCredential(input, selection, diagnostics);\n const request =\n selection.operation.protocol === 'graphql'\n ? buildGraphQlRequest(input, selection, credential, diagnostics)\n : buildHttpRequest(input, selection, credential, diagnostics);\n\n if (request === undefined || diagnostics.some((diagnostic) => diagnostic.severity === 'error')) {\n return failure(request, diagnostics);\n }\n return { ok: true, request, diagnostics };\n}\n\nfunction selectEndpointOperation(input: EndpointTestInput): EndpointOperationSelection | undefined {\n const endpoint = input.dataSource.endpoints[input.endpointId];\n const operation = endpoint?.operations[input.operationId];\n return endpoint === undefined || operation === undefined ? undefined : { endpoint, operation };\n}\n\nasync function resolveEndpointCredential(\n input: EndpointTestInput,\n selection: EndpointOperationSelection,\n diagnostics: DataSourceDiagnostic[],\n): Promise<EndpointTestCredential | undefined> {\n const ref =\n selection.operation.credential ?? selection.endpoint.credential ?? input.dataSource.credential;\n if (ref === undefined) return undefined;\n if (input.credentialResolver === undefined) {\n diagnostics.push(\n createDiagnostic(input, 'missing-credential', `Credential '${ref.id}' requires a resolver.`),\n );\n return undefined;\n }\n const credential = await input.credentialResolver(ref);\n if (credential === undefined) {\n diagnostics.push(\n createDiagnostic(\n input,\n 'missing-credential',\n `Credential '${ref.id}' could not be resolved.`,\n ),\n );\n }\n return credential;\n}\n\nfunction buildHttpRequest(\n input: EndpointTestInput,\n selection: EndpointOperationSelection,\n credential: EndpointTestCredential | undefined,\n diagnostics: DataSourceDiagnostic[],\n): EndpointTestRequestDiagnostic | undefined {\n const baseUrl = getDataSourceBaseUrl(input.dataSource, selection.endpoint);\n const path = selection.operation.path ?? selection.endpoint.path;\n if (!baseUrl) {\n diagnostics.push(\n createDiagnostic(input, 'invalid-config', 'HTTP endpoint requires a base URL.'),\n );\n return undefined;\n }\n if (path === undefined) {\n diagnostics.push(createDiagnostic(input, 'invalid-config', 'HTTP operation requires a path.'));\n return undefined;\n }\n\n const values = input.values ?? {};\n const parameters = selection.operation.request?.parameters ?? [];\n const resolvedPath = interpolatePath(path, parameters, values, input, diagnostics);\n const query = collectQuery(parameters, values, credential?.query);\n const body = createHttpBody(selection.operation, values);\n const headers: Record<string, string> = { ...(credential?.headers ?? {}) };\n if (body !== undefined) headers['content-type'] = headers['content-type'] ?? 'application/json';\n\n return {\n dataSourceId: input.dataSource.id,\n endpointId: input.endpointId,\n operationId: input.operationId,\n url: appendQuery(joinUrl(baseUrl, resolvedPath), query),\n method: selection.operation.method ?? 'GET',\n headers,\n body,\n dryRun: input.dryRun === true,\n };\n}\n\nfunction buildGraphQlRequest(\n input: EndpointTestInput,\n selection: EndpointOperationSelection,\n credential: EndpointTestCredential | undefined,\n diagnostics: DataSourceDiagnostic[],\n): EndpointTestRequestDiagnostic | undefined {\n const endpointUrl = getGraphQlEndpointUrl(input.dataSource, selection.endpoint);\n if (!endpointUrl) {\n diagnostics.push(\n createDiagnostic(input, 'invalid-config', 'GraphQL endpoint requires an endpoint URL.'),\n );\n return undefined;\n }\n\n const query =\n getStringMetadataValue(selection.operation.metadata, 'document') ??\n getStringValue(input.values, 'query');\n if (query === undefined) {\n diagnostics.push(\n createDiagnostic(input, 'invalid-config', 'GraphQL operation requires a query document.'),\n );\n return undefined;\n }\n\n const variables = getRecordValue(input.values, 'variables');\n return {\n dataSourceId: input.dataSource.id,\n endpointId: input.endpointId,\n operationId: input.operationId,\n url: appendQuery(endpointUrl, credential?.query ?? {}),\n method: 'POST',\n headers: { ...(credential?.headers ?? {}), 'content-type': 'application/json' },\n body: JSON.stringify({ query, variables: variables ?? {} }),\n dryRun: input.dryRun === true,\n };\n}\n\nfunction getDataSourceBaseUrl(\n source: DataSourceConfig,\n endpoint: DataEndpointConfig,\n): string | undefined {\n if (endpoint.baseUrl !== undefined) return endpoint.baseUrl;\n if (source.kind !== 'api' || source.origin !== 'external') return undefined;\n return source.protocol === 'rest' ? source.baseUrl : source.endpointUrl;\n}\n\nfunction getGraphQlEndpointUrl(\n source: DataSourceConfig,\n endpoint: DataEndpointConfig,\n): string | undefined {\n if (endpoint.baseUrl !== undefined) return endpoint.baseUrl;\n return source.kind === 'api' && source.origin === 'external' && source.protocol === 'graphql'\n ? source.endpointUrl\n : undefined;\n}\n\nfunction interpolatePath(\n path: string,\n parameters: readonly DataOperationParameter[],\n values: EndpointTestInputValues,\n input: EndpointTestInput,\n diagnostics: DataSourceDiagnostic[],\n): string {\n let next = path;\n for (const parameter of parameters.filter((item) => item.location === 'path')) {\n const value = values[parameter.name] ?? parameter.default;\n if (value === undefined) {\n diagnostics.push(\n createDiagnostic(\n input,\n 'invalid-config',\n `Missing required path parameter '${parameter.name}'.`,\n ),\n );\n continue;\n }\n const encoded = encodeURIComponent(serializeEndpointUrlValue(value));\n next = next\n .replaceAll(`{${parameter.name}}`, encoded)\n .replaceAll(`:${parameter.name}`, encoded);\n }\n return next;\n}\n\nfunction collectQuery(\n parameters: readonly DataOperationParameter[],\n values: EndpointTestInputValues,\n credentialQuery: EndpointTestInputValues | undefined,\n): EndpointTestInputValues {\n const query: Record<string, DataContractValue> = { ...(credentialQuery ?? {}) };\n for (const parameter of parameters) {\n if (parameter.location !== 'query') continue;\n const value = values[parameter.name] ?? parameter.default;\n if (value !== undefined) query[parameter.name] = value;\n }\n return query;\n}\n\nfunction createHttpBody(\n operation: DataOperationConfig,\n values: EndpointTestInputValues,\n): string | undefined {\n if (operation.method === 'GET' || operation.method === 'HEAD') return undefined;\n return values.body === undefined ? undefined : JSON.stringify(values.body);\n}\n\nasync function createResponseDiagnostic(\n response: EndpointTestFetchResponse,\n): Promise<EndpointTestResponseDiagnostic> {\n const bodyText = await response.text();\n return {\n status: response.status,\n ok: response.status >= 200 && response.status < 300,\n headers: response.headers,\n bodyText,\n parsedBody: parseResponseBody(bodyText),\n };\n}\n\nfunction parseResponseBody(bodyText: string): DataContractValue | undefined {\n if (bodyText.trim().length === 0) return undefined;\n try {\n const parsed: unknown = JSON.parse(bodyText);\n return isDataContractValue(parsed) ? parsed : { raw: bodyText };\n } catch {\n return { raw: bodyText };\n }\n}\n\nfunction appendQuery(url: string, query: EndpointTestInputValues): string {\n const entries = Object.entries(query);\n if (entries.length === 0) return url;\n const search = entries\n .map(\n ([key, value]) =>\n `${encodeURIComponent(key)}=${encodeURIComponent(serializeEndpointUrlValue(value))}`,\n )\n .join('&');\n return `${url}${url.includes('?') ? '&' : '?'}${search}`;\n}\n\nfunction serializeEndpointUrlValue(value: DataContractValue): string {\n if (typeof value === 'string') return value;\n if (typeof value === 'number' || typeof value === 'boolean') return value.toString();\n if (value === null) return '';\n return JSON.stringify(value);\n}\n\nfunction joinUrl(baseUrl: string, path: string): string {\n const normalizedBaseUrl = baseUrl.endsWith('/') ? baseUrl.slice(0, -1) : baseUrl;\n const normalizedPath = path.startsWith('/') ? path : `/${path}`;\n return `${normalizedBaseUrl}${normalizedPath}`;\n}\n\nfunction getStringValue(\n values: EndpointTestInputValues | undefined,\n key: string,\n): string | undefined {\n const value = values?.[key];\n return typeof value === 'string' ? value : undefined;\n}\n\nfunction getRecordValue(\n values: EndpointTestInputValues | undefined,\n key: string,\n): DataContractValue | undefined {\n const value = values?.[key];\n return isDataContractValue(value) ? value : undefined;\n}\n\nfunction getStringMetadataValue(\n metadata: DataContractValue | undefined,\n key: string,\n): string | undefined {\n if (!isDataContractRecord(metadata)) return undefined;\n const value = metadata[key];\n return typeof value === 'string' ? value : undefined;\n}\n\nfunction createDiagnostic(\n input: EndpointTestInput,\n code: string,\n message: string,\n): DataSourceDiagnostic {\n return {\n code,\n dataSourceId: input.dataSource.id,\n endpointId: input.endpointId,\n operationId: input.operationId,\n message,\n severity: code === 'missing-schema' ? 'info' : 'error',\n };\n}\n\nfunction failure(\n request: EndpointTestRequestDiagnostic | undefined,\n diagnostics: readonly DataSourceDiagnostic[],\n response?: EndpointTestResponseDiagnostic,\n): EndpointTestResult {\n return { ok: false, request, response, diagnostics };\n}\n\nfunction createNetworkErrorMessage(error: unknown): string {\n return error instanceof Error ? error.message : 'Endpoint test request failed.';\n}\n\nfunction isDataContractValue(value: unknown): value is DataContractValue {\n if (value === null || ['string', 'number', 'boolean'].includes(typeof value)) return true;\n if (Array.isArray(value)) return value.every(isDataContractValue);\n return typeof value === 'object' && Object.values(value).every(isDataContractValue);\n}\n\nfunction isDataContractRecord(\n value: DataContractValue | undefined,\n): value is Record<string, DataContractValue> {\n return (\n value !== undefined && typeof value === 'object' && value !== null && !Array.isArray(value)\n );\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ankhorage/data-sources",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "1.0.0",
|
|
5
5
|
"description": "Headless toolkit for importing, generating, normalizing, and testing Ankhorage data-source configs.",
|
|
6
6
|
"homepage": "https://github.com/ankhorage/data-sources#readme",
|
|
7
7
|
"bugs": {
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
"typescript"
|
|
26
26
|
],
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@ankhorage/contracts": "^4.0.
|
|
28
|
+
"@ankhorage/contracts": "^4.0.2"
|
|
29
29
|
},
|
|
30
30
|
"ankh": {
|
|
31
31
|
"category": "data-sources",
|