@ankhorage/data-sources 1.0.0 → 2.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.
Files changed (114) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +6 -41
  3. package/dist/ankh.provider.js +1 -1
  4. package/dist/ankh.provider.js.map +1 -1
  5. package/dist/discovery/http.d.ts +10 -0
  6. package/dist/discovery/http.d.ts.map +1 -0
  7. package/dist/discovery/http.js +35 -0
  8. package/dist/discovery/http.js.map +1 -0
  9. package/dist/discovery/index.d.ts +9 -9
  10. package/dist/discovery/index.d.ts.map +1 -1
  11. package/dist/discovery/index.js +27 -55
  12. package/dist/discovery/index.js.map +1 -1
  13. package/dist/graphql/index.d.ts +8 -76
  14. package/dist/graphql/index.d.ts.map +1 -1
  15. package/dist/graphql/index.js +17 -197
  16. package/dist/graphql/index.js.map +1 -1
  17. package/dist/graphql/introspection.d.ts +5 -0
  18. package/dist/graphql/introspection.d.ts.map +1 -0
  19. package/dist/graphql/introspection.js +136 -0
  20. package/dist/graphql/introspection.js.map +1 -0
  21. package/dist/graphql/operation.d.ts +5 -0
  22. package/dist/graphql/operation.d.ts.map +1 -0
  23. package/dist/graphql/operation.js +39 -0
  24. package/dist/graphql/operation.js.map +1 -0
  25. package/dist/graphql/types.d.ts +71 -0
  26. package/dist/graphql/types.d.ts.map +1 -0
  27. package/dist/graphql/types.js +2 -0
  28. package/dist/graphql/types.js.map +1 -0
  29. package/dist/index.d.ts +1 -17
  30. package/dist/index.d.ts.map +1 -1
  31. package/dist/index.js +1 -34
  32. package/dist/index.js.map +1 -1
  33. package/dist/openapi/endpoints.d.ts +5 -0
  34. package/dist/openapi/endpoints.d.ts.map +1 -0
  35. package/dist/openapi/endpoints.js +77 -0
  36. package/dist/openapi/endpoints.js.map +1 -0
  37. package/dist/openapi/index.d.ts +7 -87
  38. package/dist/openapi/index.d.ts.map +1 -1
  39. package/dist/openapi/index.js +8 -267
  40. package/dist/openapi/index.js.map +1 -1
  41. package/dist/openapi/operation.d.ts +5 -0
  42. package/dist/openapi/operation.d.ts.map +1 -0
  43. package/dist/openapi/operation.js +141 -0
  44. package/dist/openapi/operation.js.map +1 -0
  45. package/dist/openapi/schema.d.ts +5 -0
  46. package/dist/openapi/schema.d.ts.map +1 -0
  47. package/dist/openapi/schema.js +84 -0
  48. package/dist/openapi/schema.js.map +1 -0
  49. package/dist/openapi/types.d.ts +82 -0
  50. package/dist/openapi/types.d.ts.map +1 -0
  51. package/dist/openapi/types.js +2 -0
  52. package/dist/openapi/types.js.map +1 -0
  53. package/dist/packageInfo.d.ts +18 -0
  54. package/dist/packageInfo.d.ts.map +1 -0
  55. package/dist/packageInfo.js +30 -0
  56. package/dist/packageInfo.js.map +1 -0
  57. package/dist/rest/index.d.ts +5 -5
  58. package/dist/rest/index.d.ts.map +1 -1
  59. package/dist/rest/index.js +49 -48
  60. package/dist/rest/index.js.map +1 -1
  61. package/dist/test-runner/index.d.ts +2 -57
  62. package/dist/test-runner/index.d.ts.map +1 -1
  63. package/dist/test-runner/index.js +58 -160
  64. package/dist/test-runner/index.js.map +1 -1
  65. package/dist/test-runner/response.d.ts +3 -0
  66. package/dist/test-runner/response.d.ts.map +1 -0
  67. package/dist/test-runner/response.js +29 -0
  68. package/dist/test-runner/response.js.map +1 -0
  69. package/dist/test-runner/result.d.ts +6 -0
  70. package/dist/test-runner/result.d.ts.map +1 -0
  71. package/dist/test-runner/result.js +17 -0
  72. package/dist/test-runner/result.js.map +1 -0
  73. package/dist/test-runner/serialization.d.ts +12 -0
  74. package/dist/test-runner/serialization.d.ts.map +1 -0
  75. package/dist/test-runner/serialization.js +99 -0
  76. package/dist/test-runner/serialization.js.map +1 -0
  77. package/dist/test-runner/types.d.ts +58 -0
  78. package/dist/test-runner/types.d.ts.map +1 -0
  79. package/dist/test-runner/types.js +2 -0
  80. package/dist/test-runner/types.js.map +1 -0
  81. package/package.json +5 -5
  82. package/src/ankh.provider.ts +1 -1
  83. package/src/api-phase1.contract.test.ts +24 -0
  84. package/src/discovery/http.ts +38 -0
  85. package/src/discovery/index.test.ts +76 -75
  86. package/src/discovery/index.ts +49 -72
  87. package/src/graphql/index.test.ts +10 -11
  88. package/src/graphql/index.ts +35 -315
  89. package/src/graphql/introspection.ts +163 -0
  90. package/src/graphql/operation.ts +60 -0
  91. package/src/graphql/types.ts +79 -0
  92. package/src/index.test.ts +21 -13
  93. package/src/index.ts +1 -54
  94. package/src/openapi/endpoints.ts +124 -0
  95. package/src/openapi/index.test.ts +47 -47
  96. package/src/openapi/index.ts +12 -467
  97. package/src/openapi/operation.ts +231 -0
  98. package/src/openapi/schema.ts +127 -0
  99. package/src/openapi/types.ts +93 -0
  100. package/src/packageInfo.ts +50 -0
  101. package/src/rest/index.test.ts +17 -14
  102. package/src/rest/index.ts +67 -61
  103. package/src/test-runner/index.test.ts +153 -120
  104. package/src/test-runner/index.ts +97 -299
  105. package/src/test-runner/response.ts +32 -0
  106. package/src/test-runner/result.ts +35 -0
  107. package/src/test-runner/serialization.ts +151 -0
  108. package/src/test-runner/types.ts +81 -0
  109. package/dist/generated-api/index.d.ts +0 -10
  110. package/dist/generated-api/index.d.ts.map +0 -1
  111. package/dist/generated-api/index.js +0 -272
  112. package/dist/generated-api/index.js.map +0 -1
  113. package/src/generated-api/index.test.ts +0 -173
  114. package/src/generated-api/index.ts +0 -410
@@ -21,13 +21,13 @@ export function extractRestPathParams(path) {
21
21
  }
22
22
  return [...params];
23
23
  }
24
- export function validateManualRestDataSource(definition) {
24
+ export function validateManualRestApi(definition) {
25
25
  const diagnostics = [];
26
26
  if (definition.baseUrl.trim().length === 0) {
27
27
  diagnostics.push({
28
28
  code: 'invalid-config',
29
- dataSourceId: definition.id,
30
- message: 'Manual REST data source requires a non-empty baseUrl.',
29
+ apiId: definition.id,
30
+ message: 'Manual REST API requires a non-empty baseUrl.',
31
31
  path: 'baseUrl',
32
32
  severity: 'error',
33
33
  });
@@ -36,7 +36,7 @@ export function validateManualRestDataSource(definition) {
36
36
  if (!endpoint.path.startsWith('/')) {
37
37
  diagnostics.push({
38
38
  code: 'invalid-config',
39
- dataSourceId: definition.id,
39
+ apiId: definition.id,
40
40
  endpointId: endpoint.id,
41
41
  message: 'Manual REST endpoint paths must start with `/`.',
42
42
  path: `endpoints.${endpoint.id}.path`,
@@ -49,57 +49,21 @@ export function validateManualRestDataSource(definition) {
49
49
  }
50
50
  return diagnostics;
51
51
  }
52
- export function createManualRestDataSource(definition) {
53
- const diagnostics = validateManualRestDataSource(definition);
52
+ export function createManualRestApi(definition) {
53
+ const diagnostics = validateManualRestApi(definition);
54
54
  if (diagnostics.some((diagnostic) => diagnostic.severity === 'error')) {
55
55
  return { ok: false, diagnostics };
56
56
  }
57
57
  return {
58
58
  ok: true,
59
- data: normalizeManualRestDataSource(definition),
59
+ data: normalizeManualRestApi(definition),
60
60
  diagnostics,
61
61
  };
62
62
  }
63
- export function normalizeManualRestDataSource(definition) {
64
- const endpoints = {};
65
- for (const endpoint of definition.endpoints) {
66
- const operations = {};
67
- for (const operation of endpoint.operations) {
68
- const { parameters } = operation;
69
- const request = operation.request === undefined && parameters === undefined
70
- ? undefined
71
- : { ...operation.request, parameters };
72
- operations[operation.id] = {
73
- id: operation.id,
74
- endpointId: endpoint.id,
75
- name: operation.name,
76
- description: operation.description,
77
- protocol: 'http',
78
- intent: operation.intent,
79
- method: normalizeManualRestMethod(operation.method),
80
- path: operation.path ?? endpoint.path,
81
- request,
82
- response: operation.response,
83
- pagination: operation.pagination,
84
- credential: operation.credential,
85
- metadata: operation.metadata,
86
- };
87
- }
88
- endpoints[endpoint.id] = {
89
- id: endpoint.id,
90
- kind: 'http',
91
- name: endpoint.name,
92
- description: endpoint.description,
93
- baseUrl: definition.baseUrl,
94
- path: endpoint.path,
95
- credential: endpoint.credential,
96
- operations,
97
- metadata: endpoint.metadata,
98
- };
99
- }
63
+ export function normalizeManualRestApi(definition) {
64
+ const endpoints = Object.fromEntries(definition.endpoints.map((endpoint) => [endpoint.id, normalizeManualRestEndpoint(endpoint)]));
100
65
  return {
101
66
  id: definition.id,
102
- kind: 'api',
103
67
  origin: 'external',
104
68
  protocol: 'rest',
105
69
  name: definition.name,
@@ -111,6 +75,43 @@ export function normalizeManualRestDataSource(definition) {
111
75
  metadata: definition.metadata,
112
76
  };
113
77
  }
78
+ function normalizeManualRestEndpoint(endpoint) {
79
+ const operations = Object.fromEntries(endpoint.operations.map((operation) => [
80
+ operation.id,
81
+ normalizeManualRestOperation(endpoint, operation),
82
+ ]));
83
+ return {
84
+ id: endpoint.id,
85
+ kind: 'http',
86
+ name: endpoint.name,
87
+ description: endpoint.description,
88
+ path: endpoint.path,
89
+ credential: endpoint.credential,
90
+ operations,
91
+ metadata: endpoint.metadata,
92
+ };
93
+ }
94
+ function normalizeManualRestOperation(endpoint, operation) {
95
+ const { parameters } = operation;
96
+ const request = operation.request === undefined && parameters === undefined
97
+ ? undefined
98
+ : { ...operation.request, parameters };
99
+ return {
100
+ id: operation.id,
101
+ endpointId: endpoint.id,
102
+ name: operation.name,
103
+ description: operation.description,
104
+ protocol: 'http',
105
+ intent: operation.intent,
106
+ method: normalizeManualRestMethod(operation.method),
107
+ path: operation.path ?? endpoint.path,
108
+ request,
109
+ response: operation.response,
110
+ pagination: operation.pagination,
111
+ credential: operation.credential,
112
+ metadata: operation.metadata,
113
+ };
114
+ }
114
115
  function validateManualRestOperation(definition, endpoint, operation, diagnostics) {
115
116
  const method = normalizeManualRestMethod(operation.method);
116
117
  const path = operation.path ?? endpoint.path;
@@ -120,7 +121,7 @@ function validateManualRestOperation(definition, endpoint, operation, diagnostic
120
121
  if (!isManualRestMethod(method)) {
121
122
  diagnostics.push({
122
123
  code: 'invalid-config',
123
- dataSourceId: definition.id,
124
+ apiId: definition.id,
124
125
  endpointId: endpoint.id,
125
126
  operationId: operation.id,
126
127
  message: `Manual REST operation method must be one of ${REST_METHODS.join(', ')}.`,
@@ -133,7 +134,7 @@ function validateManualRestOperation(definition, endpoint, operation, diagnostic
133
134
  continue;
134
135
  diagnostics.push({
135
136
  code: 'invalid-config',
136
- dataSourceId: definition.id,
137
+ apiId: definition.id,
137
138
  endpointId: endpoint.id,
138
139
  operationId: operation.id,
139
140
  message: `Path template parameter '${templateParam}' must have a matching path parameter definition.`,
@@ -146,7 +147,7 @@ function validateManualRestOperation(definition, endpoint, operation, diagnostic
146
147
  continue;
147
148
  diagnostics.push({
148
149
  code: 'invalid-config',
149
- dataSourceId: definition.id,
150
+ apiId: definition.id,
150
151
  endpointId: endpoint.id,
151
152
  operationId: operation.id,
152
153
  message: `Path parameter '${pathParameter.name}' is not referenced by the operation path template.`,
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/rest/index.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n CredentialRef,\n DataContractValue,\n DataEndpointConfig,\n DataOperationConfig,\n DataOperationIntent,\n DataOperationMethod,\n DataOperationPagination,\n DataOperationParameter,\n DataOperationRequest,\n DataOperationResponse,\n DataSchemaRegistry,\n DataSourceDiagnostic,\n DataSourceDiagnosticResult,\n EndpointId,\n ExternalRestApiDataSourceConfig,\n OperationId,\n} from '@ankhorage/contracts/data';\n\nconst REST_METHODS = ['DELETE', 'GET', 'HEAD', 'OPTIONS', 'PATCH', 'POST', 'PUT'] as const;\n\nexport type ManualRestMethod = (typeof REST_METHODS)[number];\n\nexport interface ManualRestOperationDefinition {\n readonly id: OperationId;\n readonly intent: DataOperationIntent;\n readonly method: string;\n readonly name?: string;\n readonly description?: string;\n readonly path?: string;\n readonly credential?: CredentialRef;\n readonly parameters?: readonly DataOperationParameter[];\n readonly request?: DataOperationRequest;\n readonly response?: DataOperationResponse;\n readonly pagination?: DataOperationPagination;\n readonly metadata?: DataContractValue;\n}\n\nexport interface ManualRestEndpointDefinition {\n readonly id: EndpointId;\n readonly path: string;\n readonly name?: string;\n readonly description?: string;\n readonly credential?: CredentialRef;\n readonly operations: readonly ManualRestOperationDefinition[];\n readonly metadata?: DataContractValue;\n}\n\nexport interface ManualRestDataSourceDefinition {\n readonly id: string;\n readonly baseUrl: string;\n readonly name?: string;\n readonly description?: string;\n readonly credential?: CredentialRef;\n readonly endpoints: readonly ManualRestEndpointDefinition[];\n readonly schemas?: DataSchemaRegistry;\n readonly metadata?: DataContractValue;\n}\n\nexport function isManualRestMethod(method: string): method is ManualRestMethod {\n return REST_METHODS.some((restMethod) => restMethod === method);\n}\n\nexport function normalizeManualRestMethod(method: string): DataOperationMethod {\n return method.toUpperCase();\n}\n\nexport function extractRestPathParams(path: string): readonly string[] {\n const params = new Set<string>();\n const bracedParamPattern = /\\{([A-Za-z_][A-Za-z0-9_]*)\\}/g;\n const colonParamPattern = /(^|\\/):([A-Za-z_][A-Za-z0-9_]*)/g;\n\n for (const match of path.matchAll(bracedParamPattern)) {\n const [, param] = match;\n if (param !== undefined) params.add(param);\n }\n\n for (const match of path.matchAll(colonParamPattern)) {\n const [, , param] = match;\n if (param !== undefined) params.add(param);\n }\n\n return [...params];\n}\n\nexport function validateManualRestDataSource(\n definition: ManualRestDataSourceDefinition,\n): readonly DataSourceDiagnostic[] {\n const diagnostics: DataSourceDiagnostic[] = [];\n\n if (definition.baseUrl.trim().length === 0) {\n diagnostics.push({\n code: 'invalid-config',\n dataSourceId: definition.id,\n message: 'Manual REST data source requires a non-empty baseUrl.',\n path: 'baseUrl',\n severity: 'error',\n });\n }\n\n for (const endpoint of definition.endpoints) {\n if (!endpoint.path.startsWith('/')) {\n diagnostics.push({\n code: 'invalid-config',\n dataSourceId: definition.id,\n endpointId: endpoint.id,\n message: 'Manual REST endpoint paths must start with `/`.',\n path: `endpoints.${endpoint.id}.path`,\n severity: 'error',\n });\n }\n\n for (const operation of endpoint.operations) {\n validateManualRestOperation(definition, endpoint, operation, diagnostics);\n }\n }\n\n return diagnostics;\n}\n\nexport function createManualRestDataSource(\n definition: ManualRestDataSourceDefinition,\n): DataSourceDiagnosticResult<ExternalRestApiDataSourceConfig> {\n const diagnostics = validateManualRestDataSource(definition);\n if (diagnostics.some((diagnostic) => diagnostic.severity === 'error')) {\n return { ok: false, diagnostics };\n }\n\n return {\n ok: true,\n data: normalizeManualRestDataSource(definition),\n diagnostics,\n };\n}\n\nexport function normalizeManualRestDataSource(\n definition: ManualRestDataSourceDefinition,\n): ExternalRestApiDataSourceConfig {\n const endpoints: Record<EndpointId, DataEndpointConfig> = {};\n\n for (const endpoint of definition.endpoints) {\n const operations: Record<OperationId, DataOperationConfig> = {};\n\n for (const operation of endpoint.operations) {\n const { parameters } = operation;\n const request: DataOperationRequest | undefined =\n operation.request === undefined && parameters === undefined\n ? undefined\n : { ...operation.request, parameters };\n\n operations[operation.id] = {\n id: operation.id,\n endpointId: endpoint.id,\n name: operation.name,\n description: operation.description,\n protocol: 'http',\n intent: operation.intent,\n method: normalizeManualRestMethod(operation.method),\n path: operation.path ?? endpoint.path,\n request,\n response: operation.response,\n pagination: operation.pagination,\n credential: operation.credential,\n metadata: operation.metadata,\n };\n }\n\n endpoints[endpoint.id] = {\n id: endpoint.id,\n kind: 'http',\n name: endpoint.name,\n description: endpoint.description,\n baseUrl: definition.baseUrl,\n path: endpoint.path,\n credential: endpoint.credential,\n operations,\n metadata: endpoint.metadata,\n };\n }\n\n return {\n id: definition.id,\n kind: 'api',\n origin: 'external',\n protocol: 'rest',\n name: definition.name,\n description: definition.description,\n baseUrl: definition.baseUrl,\n credential: definition.credential,\n endpoints,\n schemas: definition.schemas,\n metadata: definition.metadata,\n };\n}\n\nfunction validateManualRestOperation(\n definition: ManualRestDataSourceDefinition,\n endpoint: ManualRestEndpointDefinition,\n operation: ManualRestOperationDefinition,\n diagnostics: DataSourceDiagnostic[],\n): void {\n const method = normalizeManualRestMethod(operation.method);\n const path = operation.path ?? endpoint.path;\n const templateParams = extractRestPathParams(path);\n const pathParameters =\n operation.parameters?.filter((parameter) => parameter.location === 'path') ?? [];\n const pathParameterNames = new Set(pathParameters.map((parameter) => parameter.name));\n\n if (!isManualRestMethod(method)) {\n diagnostics.push({\n code: 'invalid-config',\n dataSourceId: definition.id,\n endpointId: endpoint.id,\n operationId: operation.id,\n message: `Manual REST operation method must be one of ${REST_METHODS.join(', ')}.`,\n path: `endpoints.${endpoint.id}.operations.${operation.id}.method`,\n severity: 'error',\n });\n }\n\n for (const templateParam of templateParams) {\n if (pathParameterNames.has(templateParam)) continue;\n diagnostics.push({\n code: 'invalid-config',\n dataSourceId: definition.id,\n endpointId: endpoint.id,\n operationId: operation.id,\n message: `Path template parameter '${templateParam}' must have a matching path parameter definition.`,\n path: `endpoints.${endpoint.id}.operations.${operation.id}.parameters`,\n severity: 'error',\n });\n }\n\n for (const pathParameter of pathParameters) {\n if (templateParams.includes(pathParameter.name)) continue;\n diagnostics.push({\n code: 'invalid-config',\n dataSourceId: definition.id,\n endpointId: endpoint.id,\n operationId: operation.id,\n message: `Path parameter '${pathParameter.name}' is not referenced by the operation path template.`,\n path: `endpoints.${endpoint.id}.operations.${operation.id}.parameters.${pathParameter.name}`,\n severity: 'error',\n });\n }\n}\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/rest/index.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n CredentialRef,\n DataContractValue,\n DataEndpointConfig,\n DataOperationConfig,\n DataOperationIntent,\n DataOperationMethod,\n DataOperationPagination,\n DataOperationParameter,\n DataOperationRequest,\n DataOperationResponse,\n DataSchemaRegistry,\n DataSourceDiagnostic,\n DataSourceDiagnosticResult,\n EndpointId,\n ExternalRestApiDefinition,\n OperationId,\n} from '@ankhorage/contracts/data';\n\nconst REST_METHODS = ['DELETE', 'GET', 'HEAD', 'OPTIONS', 'PATCH', 'POST', 'PUT'] as const;\n\nexport type ManualRestMethod = (typeof REST_METHODS)[number];\n\nexport interface ManualRestOperationDefinition {\n readonly id: OperationId;\n readonly intent: DataOperationIntent;\n readonly method: string;\n readonly name?: string;\n readonly description?: string;\n readonly path?: string;\n readonly credential?: CredentialRef;\n readonly parameters?: readonly DataOperationParameter[];\n readonly request?: DataOperationRequest;\n readonly response?: DataOperationResponse;\n readonly pagination?: DataOperationPagination;\n readonly metadata?: DataContractValue;\n}\n\nexport interface ManualRestEndpointDefinition {\n readonly id: EndpointId;\n readonly path: string;\n readonly name?: string;\n readonly description?: string;\n readonly credential?: CredentialRef;\n readonly operations: readonly ManualRestOperationDefinition[];\n readonly metadata?: DataContractValue;\n}\n\nexport interface ManualRestApiDefinition {\n readonly id: string;\n readonly baseUrl: string;\n readonly name?: string;\n readonly description?: string;\n readonly credential?: CredentialRef;\n readonly endpoints: readonly ManualRestEndpointDefinition[];\n readonly schemas?: DataSchemaRegistry;\n readonly metadata?: DataContractValue;\n}\n\nexport function isManualRestMethod(method: string): method is ManualRestMethod {\n return REST_METHODS.some((restMethod) => restMethod === method);\n}\n\nexport function normalizeManualRestMethod(method: string): DataOperationMethod {\n return method.toUpperCase();\n}\n\nexport function extractRestPathParams(path: string): readonly string[] {\n const params = new Set<string>();\n const bracedParamPattern = /\\{([A-Za-z_][A-Za-z0-9_]*)\\}/g;\n const colonParamPattern = /(^|\\/):([A-Za-z_][A-Za-z0-9_]*)/g;\n\n for (const match of path.matchAll(bracedParamPattern)) {\n const [, param] = match;\n if (param !== undefined) params.add(param);\n }\n\n for (const match of path.matchAll(colonParamPattern)) {\n const [, , param] = match;\n if (param !== undefined) params.add(param);\n }\n\n return [...params];\n}\n\nexport function validateManualRestApi(\n definition: ManualRestApiDefinition,\n): readonly DataSourceDiagnostic[] {\n const diagnostics: DataSourceDiagnostic[] = [];\n\n if (definition.baseUrl.trim().length === 0) {\n diagnostics.push({\n code: 'invalid-config',\n apiId: definition.id,\n message: 'Manual REST API requires a non-empty baseUrl.',\n path: 'baseUrl',\n severity: 'error',\n });\n }\n\n for (const endpoint of definition.endpoints) {\n if (!endpoint.path.startsWith('/')) {\n diagnostics.push({\n code: 'invalid-config',\n apiId: definition.id,\n endpointId: endpoint.id,\n message: 'Manual REST endpoint paths must start with `/`.',\n path: `endpoints.${endpoint.id}.path`,\n severity: 'error',\n });\n }\n\n for (const operation of endpoint.operations) {\n validateManualRestOperation(definition, endpoint, operation, diagnostics);\n }\n }\n\n return diagnostics;\n}\n\nexport function createManualRestApi(\n definition: ManualRestApiDefinition,\n): DataSourceDiagnosticResult<ExternalRestApiDefinition> {\n const diagnostics = validateManualRestApi(definition);\n if (diagnostics.some((diagnostic) => diagnostic.severity === 'error')) {\n return { ok: false, diagnostics };\n }\n\n return {\n ok: true,\n data: normalizeManualRestApi(definition),\n diagnostics,\n };\n}\n\nexport function normalizeManualRestApi(\n definition: ManualRestApiDefinition,\n): ExternalRestApiDefinition {\n const endpoints = Object.fromEntries(\n definition.endpoints.map((endpoint) => [endpoint.id, normalizeManualRestEndpoint(endpoint)]),\n );\n\n return {\n id: definition.id,\n origin: 'external',\n protocol: 'rest',\n name: definition.name,\n description: definition.description,\n baseUrl: definition.baseUrl,\n credential: definition.credential,\n endpoints,\n schemas: definition.schemas,\n metadata: definition.metadata,\n };\n}\n\nfunction normalizeManualRestEndpoint(endpoint: ManualRestEndpointDefinition): DataEndpointConfig {\n const operations = Object.fromEntries(\n endpoint.operations.map((operation) => [\n operation.id,\n normalizeManualRestOperation(endpoint, operation),\n ]),\n );\n return {\n id: endpoint.id,\n kind: 'http',\n name: endpoint.name,\n description: endpoint.description,\n path: endpoint.path,\n credential: endpoint.credential,\n operations,\n metadata: endpoint.metadata,\n };\n}\n\nfunction normalizeManualRestOperation(\n endpoint: ManualRestEndpointDefinition,\n operation: ManualRestOperationDefinition,\n): DataOperationConfig {\n const { parameters } = operation;\n const request: DataOperationRequest | undefined =\n operation.request === undefined && parameters === undefined\n ? undefined\n : { ...operation.request, parameters };\n return {\n id: operation.id,\n endpointId: endpoint.id,\n name: operation.name,\n description: operation.description,\n protocol: 'http',\n intent: operation.intent,\n method: normalizeManualRestMethod(operation.method),\n path: operation.path ?? endpoint.path,\n request,\n response: operation.response,\n pagination: operation.pagination,\n credential: operation.credential,\n metadata: operation.metadata,\n };\n}\n\nfunction validateManualRestOperation(\n definition: ManualRestApiDefinition,\n endpoint: ManualRestEndpointDefinition,\n operation: ManualRestOperationDefinition,\n diagnostics: DataSourceDiagnostic[],\n): void {\n const method = normalizeManualRestMethod(operation.method);\n const path = operation.path ?? endpoint.path;\n const templateParams = extractRestPathParams(path);\n const pathParameters =\n operation.parameters?.filter((parameter) => parameter.location === 'path') ?? [];\n const pathParameterNames = new Set(pathParameters.map((parameter) => parameter.name));\n\n if (!isManualRestMethod(method)) {\n diagnostics.push({\n code: 'invalid-config',\n apiId: definition.id,\n endpointId: endpoint.id,\n operationId: operation.id,\n message: `Manual REST operation method must be one of ${REST_METHODS.join(', ')}.`,\n path: `endpoints.${endpoint.id}.operations.${operation.id}.method`,\n severity: 'error',\n });\n }\n\n for (const templateParam of templateParams) {\n if (pathParameterNames.has(templateParam)) continue;\n diagnostics.push({\n code: 'invalid-config',\n apiId: definition.id,\n endpointId: endpoint.id,\n operationId: operation.id,\n message: `Path template parameter '${templateParam}' must have a matching path parameter definition.`,\n path: `endpoints.${endpoint.id}.operations.${operation.id}.parameters`,\n severity: 'error',\n });\n }\n\n for (const pathParameter of pathParameters) {\n if (templateParams.includes(pathParameter.name)) continue;\n diagnostics.push({\n code: 'invalid-config',\n apiId: definition.id,\n endpointId: endpoint.id,\n operationId: operation.id,\n message: `Path parameter '${pathParameter.name}' is not referenced by the operation path template.`,\n path: `endpoints.${endpoint.id}.operations.${operation.id}.parameters.${pathParameter.name}`,\n severity: 'error',\n });\n }\n}\n"]}
@@ -1,60 +1,5 @@
1
- import type { CredentialRef, DataContractValue, DataSourceConfig, DataSourceDiagnostic, EndpointId, OperationId } from '@ankhorage/contracts/data';
2
- export type EndpointTestInputValues = Readonly<Record<string, DataContractValue>>;
3
- export type EndpointTestHeaders = Readonly<Record<string, string>>;
4
- export interface EndpointTestCredential {
5
- readonly headers?: EndpointTestHeaders;
6
- readonly query?: EndpointTestInputValues;
7
- }
8
- export type EndpointTestCredentialResolver = (credential: CredentialRef) => EndpointTestCredential | Promise<EndpointTestCredential | undefined> | undefined;
9
- export interface EndpointTestFetchInit {
10
- readonly method: string;
11
- readonly headers: EndpointTestHeaders;
12
- readonly body?: string;
13
- }
14
- export interface EndpointTestFetchResponse {
15
- readonly status: number;
16
- readonly headers?: EndpointTestHeaders;
17
- text(): Promise<string>;
18
- }
19
- export type EndpointTestFetch = (url: string, init: EndpointTestFetchInit) => Promise<EndpointTestFetchResponse>;
20
- export interface EndpointTestInput {
21
- readonly dataSource: DataSourceConfig;
22
- readonly endpointId: EndpointId;
23
- readonly operationId: OperationId;
24
- readonly values?: EndpointTestInputValues;
25
- readonly dryRun?: boolean;
26
- readonly fetch?: EndpointTestFetch;
27
- readonly credentialResolver?: EndpointTestCredentialResolver;
28
- }
29
- export interface EndpointTestRequestDiagnostic {
30
- readonly dataSourceId: string;
31
- readonly endpointId: EndpointId;
32
- readonly operationId: OperationId;
33
- readonly url: string;
34
- readonly method: string;
35
- readonly headers: EndpointTestHeaders;
36
- readonly body?: string;
37
- readonly dryRun: boolean;
38
- }
39
- export interface EndpointTestResponseDiagnostic {
40
- readonly status: number;
41
- readonly ok: boolean;
42
- readonly headers?: EndpointTestHeaders;
43
- readonly bodyText?: string;
44
- readonly parsedBody?: DataContractValue;
45
- }
46
- export type EndpointTestResult = {
47
- readonly ok: true;
48
- readonly request: EndpointTestRequestDiagnostic;
49
- readonly response?: EndpointTestResponseDiagnostic;
50
- readonly data?: DataContractValue;
51
- readonly diagnostics: readonly DataSourceDiagnostic[];
52
- } | {
53
- readonly ok: false;
54
- readonly request?: EndpointTestRequestDiagnostic;
55
- readonly response?: EndpointTestResponseDiagnostic;
56
- readonly diagnostics: readonly DataSourceDiagnostic[];
57
- };
1
+ import type { EndpointTestInput, EndpointTestResult } from './types';
2
+ export type * from './types';
58
3
  export declare function testEndpoint(input: EndpointTestInput): Promise<EndpointTestResult>;
59
4
  export declare function buildEndpointTestRequest(input: EndpointTestInput): Promise<EndpointTestResult>;
60
5
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/test-runner/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,aAAa,EACb,iBAAiB,EAIjB,gBAAgB,EAChB,oBAAoB,EACpB,UAAU,EACV,WAAW,EACZ,MAAM,2BAA2B,CAAC;AAEnC,MAAM,MAAM,uBAAuB,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC,CAAC;AAClF,MAAM,MAAM,mBAAmB,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;AAEnE,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,OAAO,CAAC,EAAE,mBAAmB,CAAC;IACvC,QAAQ,CAAC,KAAK,CAAC,EAAE,uBAAuB,CAAC;CAC1C;AAED,MAAM,MAAM,8BAA8B,GAAG,CAC3C,UAAU,EAAE,aAAa,KACtB,sBAAsB,GAAG,OAAO,CAAC,sBAAsB,GAAG,SAAS,CAAC,GAAG,SAAS,CAAC;AAEtF,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,OAAO,EAAE,mBAAmB,CAAC;IACtC,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,yBAAyB;IACxC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,OAAO,CAAC,EAAE,mBAAmB,CAAC;IACvC,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;CACzB;AAED,MAAM,MAAM,iBAAiB,GAAG,CAC9B,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,qBAAqB,KACxB,OAAO,CAAC,yBAAyB,CAAC,CAAC;AAExC,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,UAAU,EAAE,gBAAgB,CAAC;IACtC,QAAQ,CAAC,UAAU,EAAE,UAAU,CAAC;IAChC,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAC;IAClC,QAAQ,CAAC,MAAM,CAAC,EAAE,uBAAuB,CAAC;IAC1C,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,KAAK,CAAC,EAAE,iBAAiB,CAAC;IACnC,QAAQ,CAAC,kBAAkB,CAAC,EAAE,8BAA8B,CAAC;CAC9D;AAED,MAAM,WAAW,6BAA6B;IAC5C,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,UAAU,EAAE,UAAU,CAAC;IAChC,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAC;IAClC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,OAAO,EAAE,mBAAmB,CAAC;IACtC,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;CAC1B;AAED,MAAM,WAAW,8BAA8B;IAC7C,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,EAAE,EAAE,OAAO,CAAC;IACrB,QAAQ,CAAC,OAAO,CAAC,EAAE,mBAAmB,CAAC;IACvC,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,UAAU,CAAC,EAAE,iBAAiB,CAAC;CACzC;AAED,MAAM,MAAM,kBAAkB,GAC1B;IACE,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC;IAClB,QAAQ,CAAC,OAAO,EAAE,6BAA6B,CAAC;IAChD,QAAQ,CAAC,QAAQ,CAAC,EAAE,8BAA8B,CAAC;IACnD,QAAQ,CAAC,IAAI,CAAC,EAAE,iBAAiB,CAAC;IAClC,QAAQ,CAAC,WAAW,EAAE,SAAS,oBAAoB,EAAE,CAAC;CACvD,GACD;IACE,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC;IACnB,QAAQ,CAAC,OAAO,CAAC,EAAE,6BAA6B,CAAC;IACjD,QAAQ,CAAC,QAAQ,CAAC,EAAE,8BAA8B,CAAC;IACnD,QAAQ,CAAC,WAAW,EAAE,SAAS,oBAAoB,EAAE,CAAC;CACvD,CAAC;AAON,wBAAsB,YAAY,CAAC,KAAK,EAAE,iBAAiB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CA+CxF;AAED,wBAAsB,wBAAwB,CAC5C,KAAK,EAAE,iBAAiB,GACvB,OAAO,CAAC,kBAAkB,CAAC,CA6B7B"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/test-runner/index.ts"],"names":[],"mappings":"AAoBA,OAAO,KAAK,EAEV,iBAAiB,EAEjB,kBAAkB,EACnB,MAAM,SAAS,CAAC;AAEjB,mBAAmB,SAAS,CAAC;AAO7B,wBAAsB,YAAY,CAAC,KAAK,EAAE,iBAAiB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CA4BxF;AAED,wBAAsB,wBAAwB,CAC5C,KAAK,EAAE,iBAAiB,GACvB,OAAO,CAAC,kBAAkB,CAAC,CA8B7B"}
@@ -1,3 +1,6 @@
1
+ import { createResponseDiagnostic } from './response';
2
+ import { createDiagnostic, createNetworkErrorMessage, failure } from './result';
3
+ import { appendQuery, collectHeaders, collectQuery, createHttpBody, getRecordValue, getStringMetadataValue, getStringValue, interpolatePath, joinUrl, } from './serialization';
1
4
  export async function testEndpoint(input) {
2
5
  const built = await buildEndpointTestRequest(input);
3
6
  if (!built.ok || input.dryRun === true)
@@ -14,20 +17,7 @@ export async function testEndpoint(input) {
14
17
  headers: built.request.headers,
15
18
  body: built.request.body,
16
19
  });
17
- const responseDiagnostic = await createResponseDiagnostic(response);
18
- const diagnostics = [...built.diagnostics];
19
- if (!responseDiagnostic.ok) {
20
- diagnostics.push(createDiagnostic(input, 'http-error', `Endpoint test request returned HTTP status ${responseDiagnostic.status}.`));
21
- }
22
- return responseDiagnostic.ok
23
- ? {
24
- ok: true,
25
- request: built.request,
26
- response: responseDiagnostic,
27
- data: responseDiagnostic.parsedBody,
28
- diagnostics,
29
- }
30
- : failure(built.request, diagnostics, responseDiagnostic);
20
+ return createEndpointTestResult(input, built, await createResponseDiagnostic(response));
31
21
  }
32
22
  catch (error) {
33
23
  return failure(built.request, [
@@ -37,34 +27,51 @@ export async function testEndpoint(input) {
37
27
  }
38
28
  }
39
29
  export async function buildEndpointTestRequest(input) {
30
+ if (input.api.origin === 'internal') {
31
+ return failure(undefined, [
32
+ createDiagnostic(input, 'invalid-config', 'Internal APIs are not executable in API phase 1.'),
33
+ ]);
34
+ }
40
35
  const selection = selectEndpointOperation(input);
41
36
  if (selection === undefined) {
42
37
  return failure(undefined, [
43
38
  createDiagnostic(input, 'missing-operation', 'Endpoint or operation could not be found.'),
44
39
  ]);
45
40
  }
46
- if (selection.operation.protocol === 'database') {
41
+ if (selection.operation.protocol !== 'http' && selection.operation.protocol !== 'graphql') {
47
42
  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.'),
43
+ createDiagnostic(input, 'invalid-config', `API operation protocol '${selection.operation.protocol}' is not supported.`),
49
44
  ]);
50
45
  }
51
46
  const diagnostics = [];
52
47
  const credential = await resolveEndpointCredential(input, selection, diagnostics);
53
- const request = selection.operation.protocol === 'graphql'
54
- ? buildGraphQlRequest(input, selection, credential, diagnostics)
55
- : buildHttpRequest(input, selection, credential, diagnostics);
48
+ const request = buildProtocolRequest(input, selection, credential, diagnostics);
56
49
  if (request === undefined || diagnostics.some((diagnostic) => diagnostic.severity === 'error')) {
57
50
  return failure(request, diagnostics);
58
51
  }
59
52
  return { ok: true, request, diagnostics };
60
53
  }
54
+ function createEndpointTestResult(input, built, response) {
55
+ const diagnostics = [...built.diagnostics];
56
+ if (!response.ok) {
57
+ diagnostics.push(createDiagnostic(input, 'http-error', `Endpoint test request returned HTTP status ${response.status}.`));
58
+ return failure(built.request, diagnostics, response);
59
+ }
60
+ return {
61
+ ok: true,
62
+ request: built.request,
63
+ response,
64
+ data: response.parsedBody,
65
+ diagnostics,
66
+ };
67
+ }
61
68
  function selectEndpointOperation(input) {
62
- const endpoint = input.dataSource.endpoints[input.endpointId];
69
+ const endpoint = input.api.endpoints[input.endpointId];
63
70
  const operation = endpoint?.operations[input.operationId];
64
71
  return endpoint === undefined || operation === undefined ? undefined : { endpoint, operation };
65
72
  }
66
73
  async function resolveEndpointCredential(input, selection, diagnostics) {
67
- const ref = selection.operation.credential ?? selection.endpoint.credential ?? input.dataSource.credential;
74
+ const ref = selection.operation.credential ?? selection.endpoint.credential ?? input.api.credential;
68
75
  if (ref === undefined)
69
76
  return undefined;
70
77
  if (input.credentialResolver === undefined) {
@@ -77,39 +84,49 @@ async function resolveEndpointCredential(input, selection, diagnostics) {
77
84
  }
78
85
  return credential;
79
86
  }
87
+ function buildProtocolRequest(input, selection, credential, diagnostics) {
88
+ return selection.operation.protocol === 'graphql'
89
+ ? buildGraphQlRequest(input, selection, credential, diagnostics)
90
+ : buildHttpRequest(input, selection, credential, diagnostics);
91
+ }
80
92
  function buildHttpRequest(input, selection, credential, diagnostics) {
81
- const baseUrl = getDataSourceBaseUrl(input.dataSource, selection.endpoint);
93
+ const baseUrl = getRestBaseUrl(input.api, selection.endpoint);
82
94
  const path = selection.operation.path ?? selection.endpoint.path;
83
- if (!baseUrl) {
84
- diagnostics.push(createDiagnostic(input, 'invalid-config', 'HTTP endpoint requires a base URL.'));
85
- return undefined;
86
- }
87
- if (path === undefined) {
88
- diagnostics.push(createDiagnostic(input, 'invalid-config', 'HTTP operation requires a path.'));
95
+ if (baseUrl === undefined || path === undefined) {
96
+ const message = baseUrl === undefined
97
+ ? 'HTTP endpoint requires a base URL.'
98
+ : 'HTTP operation requires a path.';
99
+ diagnostics.push(createDiagnostic(input, 'invalid-config', message));
89
100
  return undefined;
90
101
  }
91
102
  const values = input.values ?? {};
92
103
  const parameters = selection.operation.request?.parameters ?? [];
93
104
  const resolvedPath = interpolatePath(path, parameters, values, input, diagnostics);
94
105
  const query = collectQuery(parameters, values, credential?.query);
95
- const body = createHttpBody(selection.operation, values);
96
- const headers = { ...(credential?.headers ?? {}) };
97
- if (body !== undefined)
98
- headers['content-type'] = headers['content-type'] ?? 'application/json';
106
+ const headers = collectHeaders(parameters, values, credential?.headers);
107
+ const body = createHttpBody(selection.operation, parameters, values);
108
+ const requestHeaders = body === undefined
109
+ ? headers
110
+ : {
111
+ ...headers,
112
+ 'content-type': headers['content-type'] ??
113
+ selection.operation.request?.contentType ??
114
+ 'application/json',
115
+ };
99
116
  return {
100
- dataSourceId: input.dataSource.id,
117
+ apiId: input.api.id,
101
118
  endpointId: input.endpointId,
102
119
  operationId: input.operationId,
103
120
  url: appendQuery(joinUrl(baseUrl, resolvedPath), query),
104
121
  method: selection.operation.method ?? 'GET',
105
- headers,
122
+ headers: requestHeaders,
106
123
  body,
107
124
  dryRun: input.dryRun === true,
108
125
  };
109
126
  }
110
127
  function buildGraphQlRequest(input, selection, credential, diagnostics) {
111
- const endpointUrl = getGraphQlEndpointUrl(input.dataSource, selection.endpoint);
112
- if (!endpointUrl) {
128
+ const endpointUrl = getGraphQlEndpointUrl(input.api, selection.endpoint);
129
+ if (endpointUrl === undefined) {
113
130
  diagnostics.push(createDiagnostic(input, 'invalid-config', 'GraphQL endpoint requires an endpoint URL.'));
114
131
  return undefined;
115
132
  }
@@ -121,7 +138,7 @@ function buildGraphQlRequest(input, selection, credential, diagnostics) {
121
138
  }
122
139
  const variables = getRecordValue(input.values, 'variables');
123
140
  return {
124
- dataSourceId: input.dataSource.id,
141
+ apiId: input.api.id,
125
142
  endpointId: input.endpointId,
126
143
  operationId: input.operationId,
127
144
  url: appendQuery(endpointUrl, credential?.query ?? {}),
@@ -131,133 +148,14 @@ function buildGraphQlRequest(input, selection, credential, diagnostics) {
131
148
  dryRun: input.dryRun === true,
132
149
  };
133
150
  }
134
- function getDataSourceBaseUrl(source, endpoint) {
151
+ function getRestBaseUrl(api, endpoint) {
135
152
  if (endpoint.baseUrl !== undefined)
136
153
  return endpoint.baseUrl;
137
- if (source.kind !== 'api' || source.origin !== 'external')
138
- return undefined;
139
- return source.protocol === 'rest' ? source.baseUrl : source.endpointUrl;
154
+ return api.origin === 'external' && api.protocol === 'rest' ? api.baseUrl : undefined;
140
155
  }
141
- function getGraphQlEndpointUrl(source, endpoint) {
156
+ function getGraphQlEndpointUrl(api, endpoint) {
142
157
  if (endpoint.baseUrl !== undefined)
143
158
  return endpoint.baseUrl;
144
- return source.kind === 'api' && source.origin === 'external' && source.protocol === 'graphql'
145
- ? source.endpointUrl
146
- : undefined;
147
- }
148
- function interpolatePath(path, parameters, values, input, diagnostics) {
149
- let next = path;
150
- for (const parameter of parameters.filter((item) => item.location === 'path')) {
151
- const value = values[parameter.name] ?? parameter.default;
152
- if (value === undefined) {
153
- diagnostics.push(createDiagnostic(input, 'invalid-config', `Missing required path parameter '${parameter.name}'.`));
154
- continue;
155
- }
156
- const encoded = encodeURIComponent(serializeEndpointUrlValue(value));
157
- next = next
158
- .replaceAll(`{${parameter.name}}`, encoded)
159
- .replaceAll(`:${parameter.name}`, encoded);
160
- }
161
- return next;
162
- }
163
- function collectQuery(parameters, values, credentialQuery) {
164
- const query = { ...(credentialQuery ?? {}) };
165
- for (const parameter of parameters) {
166
- if (parameter.location !== 'query')
167
- continue;
168
- const value = values[parameter.name] ?? parameter.default;
169
- if (value !== undefined)
170
- query[parameter.name] = value;
171
- }
172
- return query;
173
- }
174
- function createHttpBody(operation, values) {
175
- if (operation.method === 'GET' || operation.method === 'HEAD')
176
- return undefined;
177
- return values.body === undefined ? undefined : JSON.stringify(values.body);
178
- }
179
- async function createResponseDiagnostic(response) {
180
- const bodyText = await response.text();
181
- return {
182
- status: response.status,
183
- ok: response.status >= 200 && response.status < 300,
184
- headers: response.headers,
185
- bodyText,
186
- parsedBody: parseResponseBody(bodyText),
187
- };
188
- }
189
- function parseResponseBody(bodyText) {
190
- if (bodyText.trim().length === 0)
191
- return undefined;
192
- try {
193
- const parsed = JSON.parse(bodyText);
194
- return isDataContractValue(parsed) ? parsed : { raw: bodyText };
195
- }
196
- catch {
197
- return { raw: bodyText };
198
- }
199
- }
200
- function appendQuery(url, query) {
201
- const entries = Object.entries(query);
202
- if (entries.length === 0)
203
- return url;
204
- const search = entries
205
- .map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(serializeEndpointUrlValue(value))}`)
206
- .join('&');
207
- return `${url}${url.includes('?') ? '&' : '?'}${search}`;
208
- }
209
- function serializeEndpointUrlValue(value) {
210
- if (typeof value === 'string')
211
- return value;
212
- if (typeof value === 'number' || typeof value === 'boolean')
213
- return value.toString();
214
- if (value === null)
215
- return '';
216
- return JSON.stringify(value);
217
- }
218
- function joinUrl(baseUrl, path) {
219
- const normalizedBaseUrl = baseUrl.endsWith('/') ? baseUrl.slice(0, -1) : baseUrl;
220
- const normalizedPath = path.startsWith('/') ? path : `/${path}`;
221
- return `${normalizedBaseUrl}${normalizedPath}`;
222
- }
223
- function getStringValue(values, key) {
224
- const value = values?.[key];
225
- return typeof value === 'string' ? value : undefined;
226
- }
227
- function getRecordValue(values, key) {
228
- const value = values?.[key];
229
- return isDataContractValue(value) ? value : undefined;
230
- }
231
- function getStringMetadataValue(metadata, key) {
232
- if (!isDataContractRecord(metadata))
233
- return undefined;
234
- const value = metadata[key];
235
- return typeof value === 'string' ? value : undefined;
236
- }
237
- function createDiagnostic(input, code, message) {
238
- return {
239
- code,
240
- dataSourceId: input.dataSource.id,
241
- endpointId: input.endpointId,
242
- operationId: input.operationId,
243
- message,
244
- severity: code === 'missing-schema' ? 'info' : 'error',
245
- };
246
- }
247
- function failure(request, diagnostics, response) {
248
- return { ok: false, request, response, diagnostics };
249
- }
250
- function createNetworkErrorMessage(error) {
251
- return error instanceof Error ? error.message : 'Endpoint test request failed.';
252
- }
253
- function isDataContractValue(value) {
254
- if (value === null || ['string', 'number', 'boolean'].includes(typeof value))
255
- return true;
256
- if (Array.isArray(value))
257
- return value.every(isDataContractValue);
258
- return typeof value === 'object' && Object.values(value).every(isDataContractValue);
259
- }
260
- function isDataContractRecord(value) {
261
- return (value !== undefined && typeof value === 'object' && value !== null && !Array.isArray(value));
159
+ return api.origin === 'external' && api.protocol === 'graphql' ? api.endpointUrl : undefined;
262
160
  }
263
161
  //# sourceMappingURL=index.js.map