apollo-conn-gen 0.12.0 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/oas/io/errorsWriter.js +6 -4
- package/dist/oas/io/errorsWriter.js.map +1 -1
- package/dist/oas/io/operationWriter.d.ts +1 -0
- package/dist/oas/io/operationWriter.js +69 -8
- package/dist/oas/io/operationWriter.js.map +1 -1
- package/dist/oas/io/schemaWriter.d.ts +8 -10
- package/dist/oas/io/schemaWriter.js +25 -99
- package/dist/oas/io/schemaWriter.js.map +1 -1
- package/dist/oas/io/security.d.ts +35 -0
- package/dist/oas/io/security.js +111 -0
- package/dist/oas/io/security.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
|
@@ -26,13 +26,15 @@ export class ErrorsWriter {
|
|
|
26
26
|
// `message: "$.message"` — the path form yields the field's VALUE; a bare `message`
|
|
27
27
|
// selection would build the object `{message: …}`, which errors.message rejects. R4
|
|
28
28
|
const message = this.errorMessageField(context, op);
|
|
29
|
-
|
|
29
|
+
// label/close at the @connect arg level, the extensions body one level deeper (like queryParams)
|
|
30
|
+
const labelSpacing = ' '.repeat(indent + 6);
|
|
31
|
+
const bodySpacing = ' '.repeat(indent + 8);
|
|
30
32
|
writer
|
|
31
|
-
.write(
|
|
33
|
+
.write(labelSpacing)
|
|
32
34
|
.write(message ? `errors: { message: "$.${message}" extensions: """\n` : 'errors: { extensions: """\n')
|
|
33
|
-
.write(
|
|
35
|
+
.write(bodySpacing)
|
|
34
36
|
.write('statusCode: $status\n')
|
|
35
|
-
.write(
|
|
37
|
+
.write(labelSpacing)
|
|
36
38
|
.write('""" }\n');
|
|
37
39
|
}
|
|
38
40
|
// True when the operation documents an HTTP error response. Accepts both concrete numeric statuses
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errorsWriter.js","sourceRoot":"","sources":["../../../src/oas/io/errorsWriter.ts"],"names":[],"mappings":"AAAA,OAAO,CAAC,MAAM,QAAQ,CAAC;AAMvB,OAAO,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACnE,OAAO,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAEvC,8FAA8F;AAC9F,mCAAmC;AACnC,MAAM,OAAO,YAAY;IAKH;IAJpB,4FAA4F;IAC5F,8BAA8B;IACtB,MAAM,CAAU,cAAc,GAAG,CAAC,SAAS,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC;IAExE,YAAoB,GAAW;QAAX,QAAG,GAAH,GAAG,CAAQ;IAAG,CAAC;IAEnC,+FAA+F;IAC/F,4FAA4F;IAC5F,2DAA2D;IACpD,KAAK,CAAC,OAAmB,EAAE,MAAc,EAAE,EAAM,EAAE,MAAc;QACtE,IAAI,CAAC,OAAO,CAAC,eAAe,EAAE,mBAAmB,IAAI,CAAC,IAAI,CAAC,mBAAmB,CAAC,EAAE,CAAC,EAAE,CAAC;YACnF,OAAO;QACT,CAAC;QAED,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,oBAAoB,IAAI,gBAAgB,CAAC,oBAAoB,CAAC;QAC/F,IAAI,CAAC,YAAY,CAAC,OAAO,EAAE,MAAM,CAAC,EAAE,CAAC;YACnC,IAAI,CACF,OAAO,EACP,UAAU,EACV,0DAA0D,OAAO,sBAAsB,EAAE,CAAC,IAAI,IAAI,EAAE,CAAC,SAAS,CAAC,IAAI,EAAE,CACtH,CAAC;YACF,OAAO;QACT,CAAC;QAED,oFAAoF;QACpF,oFAAoF;QACpF,MAAM,OAAO,GAAG,IAAI,CAAC,iBAAiB,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;QAEpD,MAAM,
|
|
1
|
+
{"version":3,"file":"errorsWriter.js","sourceRoot":"","sources":["../../../src/oas/io/errorsWriter.ts"],"names":[],"mappings":"AAAA,OAAO,CAAC,MAAM,QAAQ,CAAC;AAMvB,OAAO,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACnE,OAAO,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAEvC,8FAA8F;AAC9F,mCAAmC;AACnC,MAAM,OAAO,YAAY;IAKH;IAJpB,4FAA4F;IAC5F,8BAA8B;IACtB,MAAM,CAAU,cAAc,GAAG,CAAC,SAAS,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC;IAExE,YAAoB,GAAW;QAAX,QAAG,GAAH,GAAG,CAAQ;IAAG,CAAC;IAEnC,+FAA+F;IAC/F,4FAA4F;IAC5F,2DAA2D;IACpD,KAAK,CAAC,OAAmB,EAAE,MAAc,EAAE,EAAM,EAAE,MAAc;QACtE,IAAI,CAAC,OAAO,CAAC,eAAe,EAAE,mBAAmB,IAAI,CAAC,IAAI,CAAC,mBAAmB,CAAC,EAAE,CAAC,EAAE,CAAC;YACnF,OAAO;QACT,CAAC;QAED,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,oBAAoB,IAAI,gBAAgB,CAAC,oBAAoB,CAAC;QAC/F,IAAI,CAAC,YAAY,CAAC,OAAO,EAAE,MAAM,CAAC,EAAE,CAAC;YACnC,IAAI,CACF,OAAO,EACP,UAAU,EACV,0DAA0D,OAAO,sBAAsB,EAAE,CAAC,IAAI,IAAI,EAAE,CAAC,SAAS,CAAC,IAAI,EAAE,CACtH,CAAC;YACF,OAAO;QACT,CAAC;QAED,oFAAoF;QACpF,oFAAoF;QACpF,MAAM,OAAO,GAAG,IAAI,CAAC,iBAAiB,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;QAEpD,iGAAiG;QACjG,MAAM,YAAY,GAAG,GAAG,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QAC5C,MAAM,WAAW,GAAG,GAAG,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QAC3C,MAAM;aACH,KAAK,CAAC,YAAY,CAAC;aACnB,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,yBAAyB,OAAO,qBAAqB,CAAC,CAAC,CAAC,6BAA6B,CAAC;aACtG,KAAK,CAAC,WAAW,CAAC;aAClB,KAAK,CAAC,uBAAuB,CAAC;aAC9B,KAAK,CAAC,YAAY,CAAC;aACnB,KAAK,CAAC,SAAS,CAAC,CAAC;IACtB,CAAC;IAED,mGAAmG;IACnG,mGAAmG;IACnG,wEAAwE;IAChE,mBAAmB,CAAC,EAAM;QAChC,OAAO,EAAE,CAAC,SAAS,CAAC,sBAAsB,EAAE,CAAC,IAAI,CAAC,CAAC,IAAY,EAAE,EAAE,CAAC,kBAAkB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IACrG,CAAC;IAED,4FAA4F;IAC5F,4FAA4F;IACpF,iBAAiB,CAAC,OAAmB,EAAE,EAAM;QACnD,MAAM,SAAS,GAAG,EAAE,CAAC,SAAS,CAAC,MAAM,CAAC,SAAS,IAAI,EAAE,CAAC;QACtD,MAAM,WAAW,GAAmC,EAAE,CAAC;QAEvD,KAAK,MAAM,CAAC,IAAI,EAAE,QAAQ,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,CAAC;YACzD,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;gBACnC,SAAS;YACX,CAAC;YACD,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,QAAQ,CAA0B,CAAC;YACxE,MAAM,QAAQ,GAAG,MAAM,CAAC,IAAI,CAAC,QAAQ,EAAE,OAAO,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,8BAA8B,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;YAC1G,MAAM,MAAM,GAAG,QAAQ;gBACrB,CAAC,CAAE,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,QAAS,CAAC,OAAQ,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAyB;gBACnF,CAAC,CAAC,IAAI,CAAC;YACT,IAAI,MAAM,EAAE,UAAU,EAAE,CAAC;gBACvB,WAAW,CAAC,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;YACtC,CAAC;QACH,CAAC;QAED,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC7B,OAAO,SAAS,CAAC;QACnB,CAAC;QACD,OAAO,YAAY,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAChD,WAAW,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE,CAAE,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,KAAK,CAAC,CAAyB,EAAE,IAAI,KAAK,QAAQ,CAAC,CAC5G,CAAC;IACJ,CAAC;IAED,wFAAwF;IAChF,KAAK,CAAC,OAAmB,EAAE,IAAa;QAC9C,OAAO,CAAC,CAAC,GAAG,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,EAAE,MAAM,CAAW,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAC5F,CAAC","sourcesContent":["import _ from 'lodash';\nimport { ResponseObject, SchemaObject } from 'oas/types';\nimport { OasContext } from '../oasContext.js';\nimport { OasGen } from '../oasGen.js';\nimport { Op } from '../nodes/internal.js';\nimport { Writer } from './writer.js';\nimport { DEFAULT_VERSIONS, meetsMinimum } from '../../versions.js';\nimport { warn } from '../log/trace.js';\n\n// R4 (opt-in): the `errors:` block of a connector — `message` from the documented error body,\n// `extensions` carrying `$status`.\nexport class ErrorsWriter {\n // corpus-measured priority for the error-body message field: `message` (755 error schemas),\n // `error` (362), `detail` (7)\n private static readonly MESSAGE_FIELDS = ['message', 'error', 'detail'];\n\n constructor(private gen: OasGen) {}\n\n // emit `errors: { message: \"$.message\" extensions: \"\"\"statusCode: $status\"\"\" }` for operations\n // that document HTTP error responses. errors is a connect v0.2+ feature; below that we skip\n // with a logged downgrade rather than emit invalid output.\n public write(context: OasContext, writer: Writer, op: Op, indent: number): void {\n if (!context.generateOptions?.emitConnectorErrors || !this.hasDocumentedErrors(op)) {\n return;\n }\n\n const version = this.gen.options.connectorSpecVersion || DEFAULT_VERSIONS.connectorSpecVersion;\n if (!meetsMinimum(version, 'v0.2')) {\n warn(\n context,\n '[errors]',\n `@connect(errors:) requires connect v0.2, but target is ${version} — not emitted for ${op.verb} ${op.operation.path}`,\n );\n return;\n }\n\n // `message: \"$.message\"` — the path form yields the field's VALUE; a bare `message`\n // selection would build the object `{message: …}`, which errors.message rejects. R4\n const message = this.errorMessageField(context, op);\n\n // label/close at the @connect arg level, the extensions body one level deeper (like queryParams)\n const labelSpacing = ' '.repeat(indent + 6);\n const bodySpacing = ' '.repeat(indent + 8);\n writer\n .write(labelSpacing)\n .write(message ? `errors: { message: \"$.${message}\" extensions: \"\"\"\\n` : 'errors: { extensions: \"\"\"\\n')\n .write(bodySpacing)\n .write('statusCode: $status\\n')\n .write(labelSpacing)\n .write('\"\"\" }\\n');\n }\n\n // True when the operation documents an HTTP error response. Accepts both concrete numeric statuses\n // (4xx/5xx) and the OAS range keys `4XX`/`5XX` (case-insensitive). The `default` key is excluded —\n // it also covers 2xx/3xx, so it is not specifically an error indicator.\n private hasDocumentedErrors(op: Op): boolean {\n return op.operation.getResponseStatusCodes().some((code: string) => /^[45](\\d\\d|XX)$/i.test(code));\n }\n\n // the field must be a string on EVERY documented JSON error shape — a field missing on some\n // status would yield a null message there. Non-JSON / shapeless error responses don't veto.\n private errorMessageField(context: OasContext, op: Op): string | undefined {\n const responses = op.operation.schema.responses ?? {};\n const errorShapes: Array<Record<string, unknown>> = [];\n\n for (const [code, response] of Object.entries(responses)) {\n if (!/^[45](\\d\\d|XX)$/i.test(code)) {\n continue;\n }\n const resolved = this.deref(context, response) as ResponseObject | null;\n const mediaKey = Object.keys(resolved?.content ?? {}).find((k) => /^application\\/(?:.*\\+)?json/i.test(k));\n const schema = mediaKey\n ? (this.deref(context, resolved!.content![mediaKey].schema) as SchemaObject | null)\n : null;\n if (schema?.properties) {\n errorShapes.push(schema.properties);\n }\n }\n\n if (errorShapes.length === 0) {\n return undefined;\n }\n return ErrorsWriter.MESSAGE_FIELDS.find((field) =>\n errorShapes.every((props) => (this.deref(context, props[field]) as SchemaObject | null)?.type === 'string'),\n );\n }\n\n // read-only $ref hop — resolvePointer, NOT lookupRef: this sniff must not bump refCount\n private deref(context: OasContext, node: unknown): unknown {\n return _.has(node, '$ref') ? context.resolvePointer(_.get(node, '$ref') as string) : node;\n }\n}\n"]}
|
|
@@ -2,6 +2,7 @@ import _ from 'lodash';
|
|
|
2
2
|
import { T } from '../nodes/internal.js';
|
|
3
3
|
import { Naming } from '../utils/naming.js';
|
|
4
4
|
import { ErrorsWriter } from './errorsWriter.js';
|
|
5
|
+
import { anyOperationDeclaresSecurity, globalSecurity, securitySchemes, resolveAuthHeader } from './security.js';
|
|
5
6
|
export class OperationWriter {
|
|
6
7
|
gen;
|
|
7
8
|
errorsWriter;
|
|
@@ -60,23 +61,36 @@ export class OperationWriter {
|
|
|
60
61
|
const override = context.generateOptions.overrides?.[op.id];
|
|
61
62
|
// the verb + path first, then query params, headers and lastly the body
|
|
62
63
|
writer.write(`{ ${op.verb}: `).write('"' + this.templatedPath(op, override) + '"');
|
|
63
|
-
|
|
64
|
-
this.
|
|
64
|
+
// each block starts on its own line and ends without a trailing newline
|
|
65
|
+
const wroteQueryParams = this.writeQueryParams(context, writer, op, override);
|
|
66
|
+
const wroteHeaders = this.writeHeaders(writer, op, override);
|
|
65
67
|
// body (POST, PUT, etc.): an override (raw JSONSelection) replaces the inferred
|
|
66
68
|
// `$args.input { … }` mapping; null drops the body altogether. see ROADMAP R9
|
|
67
69
|
if (typeof override?.body === 'string') {
|
|
70
|
+
// body emits its own trailing indent, so close the object right after it
|
|
68
71
|
this.writeBodyOverride(writer, override.body);
|
|
72
|
+
writer.write('}');
|
|
69
73
|
}
|
|
70
74
|
else if (override?.body === undefined && op.body) {
|
|
75
|
+
// body emits its own trailing indent, so close the object right after it
|
|
71
76
|
this.writeBodySelection(context, writer, op.body, selection);
|
|
77
|
+
writer.write('}');
|
|
78
|
+
}
|
|
79
|
+
else if (wroteQueryParams || wroteHeaders) {
|
|
80
|
+
// no body: close on its own line, aligned under `http:` (avoids a column-0 brace)
|
|
81
|
+
writer.write('\n' + ' '.repeat(6) + '}');
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
// nothing but the path: keep the compact single-line form `{ GET: "/x"}`
|
|
85
|
+
writer.write('}');
|
|
72
86
|
}
|
|
73
|
-
writer.write('}');
|
|
74
87
|
}
|
|
75
88
|
// template each {elem} as {$args.<sanitised>} (the arg name), not the raw OAS key. see docs/issues.md #2
|
|
76
89
|
// an override path may already template (`{$args.id}`, `{$config.v}`) — leave `$` segments alone
|
|
77
90
|
templatedPath(op, override) {
|
|
78
91
|
return (override?.path ?? op.operation.path).replace(/\{([^}]+)\}/g, (m, name) => name.startsWith('$') ? m : `{$args.${Naming.genParamName(name)}}`);
|
|
79
92
|
}
|
|
93
|
+
// returns true when a queryParams block was written (so the caller can place the closing brace)
|
|
80
94
|
writeQueryParams(context, writer, op, override) {
|
|
81
95
|
// we now include all query params, not just required ones. if they are not set,
|
|
82
96
|
// then the connectors will not include them in the request.
|
|
@@ -90,7 +104,7 @@ export class OperationWriter {
|
|
|
90
104
|
// e.g. `"api-version": $('2024-01')` appended, `"ids": ids->joinNotNull(";")` replaced
|
|
91
105
|
const entries = this.mergeOverrides(queryParams, override?.queryParams ?? {}, (p) => `${Naming.genParamName(p.name)}${this.arrayJoin(p)}`);
|
|
92
106
|
if (entries.length === 0) {
|
|
93
|
-
return;
|
|
107
|
+
return false;
|
|
94
108
|
}
|
|
95
109
|
writer.write('\n');
|
|
96
110
|
let spacing = ' '.repeat(6);
|
|
@@ -104,22 +118,69 @@ export class OperationWriter {
|
|
|
104
118
|
spacing = ' '.repeat(8);
|
|
105
119
|
writer.write(spacing).write('}\n');
|
|
106
120
|
spacing = ' '.repeat(6);
|
|
107
|
-
|
|
121
|
+
// no trailing newline — the caller adds the separator/brace
|
|
122
|
+
writer.write(spacing).write('"""');
|
|
123
|
+
return true;
|
|
108
124
|
}
|
|
125
|
+
// returns true when a headers block was written (so the caller can place the closing brace)
|
|
109
126
|
writeHeaders(writer, op, override) {
|
|
127
|
+
// OAS `header` params, with user overrides merged in (string replaces, null drops)
|
|
110
128
|
const headers = op.operation.getParameters().filter((p) => p.in && p.in.toLowerCase() === 'header');
|
|
111
|
-
|
|
129
|
+
let entries = this.mergeOverrides(headers, override?.headers ?? {}, (p) => this.headerExample(p));
|
|
130
|
+
// R5 slice 2: add this operation's effective auth header (only when the spec is in per-op mode).
|
|
131
|
+
// HTTP header names are case-insensitive: an explicit user override of the same name wins;
|
|
132
|
+
// otherwise the resolved auth replaces any inferred OAS header of that name (so we never emit
|
|
133
|
+
// a real credential alongside a placeholder that differs only in case).
|
|
134
|
+
const auth = this.authHeaderEntry(op);
|
|
135
|
+
if (auth) {
|
|
136
|
+
const sameName = (name) => name.toLowerCase() === auth[0].toLowerCase();
|
|
137
|
+
const overridden = Object.keys(override?.headers ?? {}).some(sameName);
|
|
138
|
+
if (!overridden) {
|
|
139
|
+
entries = [auth, ...entries.filter(([name]) => !sameName(name))];
|
|
140
|
+
}
|
|
141
|
+
}
|
|
112
142
|
if (entries.length === 0) {
|
|
113
|
-
return;
|
|
143
|
+
return false;
|
|
114
144
|
}
|
|
115
145
|
let spacing = ' '.repeat(6);
|
|
116
|
-
|
|
146
|
+
// leading newline so the block starts on its own line (after the path or a queryParams block)
|
|
147
|
+
writer.write('\n' + spacing + 'headers: [\n');
|
|
117
148
|
spacing = ' '.repeat(8);
|
|
118
149
|
for (const [key, value] of entries) {
|
|
119
150
|
writer.write(spacing + `{ name: "${key}", value: "${value}" }\n`);
|
|
120
151
|
}
|
|
121
152
|
spacing = ' '.repeat(6);
|
|
153
|
+
// no trailing newline — the caller adds the separator/brace
|
|
122
154
|
writer.write(spacing + ']');
|
|
155
|
+
return true;
|
|
156
|
+
}
|
|
157
|
+
// R5 slice 2: this operation's effective auth header as a [name, value] entry, or null.
|
|
158
|
+
//
|
|
159
|
+
// Emits only in *per-op mode* — when some operation in the spec declares its own `security`,
|
|
160
|
+
// the shared @source auth header is suppressed, so each @connect must carry its own. The
|
|
161
|
+
// effective requirement is the op's own `security` when present, else the global default:
|
|
162
|
+
// security: [{ AdminBearer: [] }] (own) -> that scheme's header (e.g. Authorization: Bearer)
|
|
163
|
+
// security: [] (public) -> no header (the op correctly sends nothing)
|
|
164
|
+
// no own `security` -> the inherited global header
|
|
165
|
+
authHeaderEntry(op) {
|
|
166
|
+
const api = this.gen.parser;
|
|
167
|
+
// uniform mode: @source already carries the shared auth, nothing to add per operation
|
|
168
|
+
if (!anyOperationDeclaresSecurity(api)) {
|
|
169
|
+
return null;
|
|
170
|
+
}
|
|
171
|
+
// the op's own requirement; `undefined` = inherit the global, `[]` = public
|
|
172
|
+
const own = op.operation.schema.security;
|
|
173
|
+
// `??` not `||`: an explicit `[]` (public) must NOT fall back to the global
|
|
174
|
+
const effective = own ?? globalSecurity(api);
|
|
175
|
+
// read the scheme definitions the requirement refers to
|
|
176
|
+
const schemes = securitySchemes(api);
|
|
177
|
+
// resolve the effective requirement to one header, collecting per-scheme drop warnings
|
|
178
|
+
const { header, warnings } = resolveAuthHeader(effective, schemes);
|
|
179
|
+
// surface dropped schemes loudly, tagged with this operation
|
|
180
|
+
for (const w of warnings) {
|
|
181
|
+
console.warn(`${w} (operation ${op.verb} ${op.operation.path})`);
|
|
182
|
+
}
|
|
183
|
+
return header ? [header.name, header.value] : null;
|
|
123
184
|
}
|
|
124
185
|
// merge user overrides over the inferred params: a string replaces the inferred value,
|
|
125
186
|
// null drops the entry, an unknown key is appended. see ROADMAP R8
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"operationWriter.js","sourceRoot":"","sources":["../../../src/oas/io/operationWriter.ts"],"names":[],"mappings":"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","sourcesContent":["import _ from 'lodash';\nimport { ParameterObject } from 'oas/types';\nimport { OasContext, RequestOverride } from '../oasContext.js';\nimport { OasGen } from '../oasGen.js';\nimport { Body, IType, Op, Param, T } from '../nodes/internal.js';\nimport { Naming } from '../utils/naming.js';\nimport { ErrorsWriter } from './errorsWriter.js';\nimport { Writer } from './writer.js';\n\nexport class OperationWriter {\n private errorsWriter: ErrorsWriter;\n\n constructor(private gen: OasGen) {\n this.errorsWriter = new ErrorsWriter(gen);\n }\n\n public writeQuery(context: OasContext, writer: Writer, collected: Map<string, IType>, selection: string[]): void {\n const selectionSet = new Set<string>(selection.map((s) => s.split('>')[0]));\n\n const paths = Array.from(collected.values()).filter((path) => selectionSet.has(path.id));\n if (_.isEmpty(paths)) return;\n\n writer.write('type Query {\\n');\n\n for (const path of paths) {\n path.generate(context, writer, []);\n this.writeConnector(context, writer, path, selection);\n context.generatedSet.add(path.id);\n }\n\n writer.write('}\\n\\n');\n }\n\n public writeMutations(context: OasContext, writer: Writer, collected: Map<string, IType>, selection: string[]): void {\n const selectionSet = new Set<string>(selection.map((s) => s.split('>')[0]));\n\n const paths = Array.from(collected.values()).filter((path) => selectionSet.has(path.id));\n if (_.isEmpty(paths)) return;\n\n writer.write('type Mutation {\\n');\n\n for (const path of paths) {\n path.generate(context, writer, []);\n this.writeConnector(context, writer, path, selection);\n context.generatedSet.add(path.id);\n }\n\n writer.write('}\\n\\n');\n }\n\n public writeConnector(context: OasContext, writer: Writer, type: IType, selection: string[]): void {\n if (!T.isOp(type)) {\n throw new Error(`expected an operation node, got ${type.id}`);\n }\n\n const op = type;\n const indent = 0;\n let spacing = ' '.repeat(indent + 4);\n writer.write(spacing).write('@connect(\\n');\n\n spacing = ' '.repeat(indent + 6);\n writer.write(spacing).write('source: \"api\"\\n').write(spacing).write('http: ');\n\n this.requestMethod(context, writer, op, selection, indent);\n\n writer.write('\\n').write(spacing).write('selection: \"\"\"\\n');\n\n // truthiness, not _.has — the declared-but-unset field is still an own property. #33\n if (op.resultType) {\n this.writeSelection(context, writer, op.resultType, selection);\n }\n\n writer.write(spacing).write('\"\"\"\\n');\n\n this.errorsWriter.write(context, writer, op, indent);\n\n spacing = ' '.repeat(indent + 4);\n writer.write(spacing).write(')\\n');\n }\n\n private requestMethod(context: OasContext, writer: Writer, op: Op, selection: string[], _indent: number): void {\n const override = context.generateOptions.overrides?.[op.id];\n\n // the verb + path first, then query params, headers and lastly the body\n writer.write(`{ ${op.verb}: `).write('\"' + this.templatedPath(op, override) + '\"');\n this.writeQueryParams(context, writer, op, override);\n this.writeHeaders(writer, op, override);\n\n // body (POST, PUT, etc.): an override (raw JSONSelection) replaces the inferred\n // `$args.input { … }` mapping; null drops the body altogether. see ROADMAP R9\n if (typeof override?.body === 'string') {\n this.writeBodyOverride(writer, override.body);\n } else if (override?.body === undefined && op.body) {\n this.writeBodySelection(context, writer, op.body, selection);\n }\n\n writer.write('}');\n }\n\n // template each {elem} as {$args.<sanitised>} (the arg name), not the raw OAS key. see docs/issues.md #2\n // an override path may already template (`{$args.id}`, `{$config.v}`) — leave `$` segments alone\n private templatedPath(op: Op, override?: RequestOverride): string {\n return (override?.path ?? op.operation.path).replace(/\\{([^}]+)\\}/g, (m, name) =>\n name.startsWith('$') ? m : `{$args.${Naming.genParamName(name)}}`,\n );\n }\n\n private writeQueryParams(context: OasContext, writer: Writer, op: Op, override?: RequestOverride): void {\n // we now include all query params, not just required ones. if they are not set,\n // then the connectors will not include them in the request.\n let queryParams = op.params.filter((p: Param) => {\n return p.parameter.in && p.parameter.in.toLowerCase() === 'query';\n });\n\n // Skip optional params if skipOptionalArgs is true\n if (context.generateOptions?.skipOptionalArgs) {\n queryParams = queryParams.filter((p: Param) => p.required);\n }\n\n // e.g. `\"api-version\": $('2024-01')` appended, `\"ids\": ids->joinNotNull(\";\")` replaced\n const entries = this.mergeOverrides(\n queryParams,\n override?.queryParams ?? {},\n (p) => `${Naming.genParamName(p.name)}${this.arrayJoin(p)}`,\n );\n if (entries.length === 0) {\n return;\n }\n\n writer.write('\\n');\n let spacing = ' '.repeat(6);\n\n writer.write(spacing).write(`queryParams: \"\"\"\\n`);\n spacing = ' '.repeat(8);\n writer.write(spacing).write(`$args {\\n`);\n spacing = ' '.repeat(10);\n for (const [key, value] of entries) {\n writer.write(spacing).write(`\"${key}\": ${value}\\n`);\n }\n spacing = ' '.repeat(8);\n writer.write(spacing).write('}\\n');\n spacing = ' '.repeat(6);\n writer.write(spacing).write('\"\"\"\\n');\n }\n\n private writeHeaders(writer: Writer, op: Op, override?: RequestOverride): void {\n const headers = op.operation.getParameters().filter((p) => p.in && p.in.toLowerCase() === 'header');\n const entries = this.mergeOverrides(headers, override?.headers ?? {}, (p) => this.headerExample(p));\n if (entries.length === 0) {\n return;\n }\n\n let spacing = ' '.repeat(6);\n writer.write(spacing + 'headers: [\\n');\n spacing = ' '.repeat(8);\n\n for (const [key, value] of entries) {\n writer.write(spacing + `{ name: \"${key}\", value: \"${value}\" }\\n`);\n }\n\n spacing = ' '.repeat(6);\n writer.write(spacing + ']');\n }\n\n // merge user overrides over the inferred params: a string replaces the inferred value,\n // null drops the entry, an unknown key is appended. see ROADMAP R8\n private mergeOverrides<T extends { name: string }>(\n inferred: T[],\n overrides: Record<string, string | null>,\n inferredValue: (item: T) => string,\n ): Array<[string, string]> {\n const kept = inferred\n .filter((item) => overrides[item.name] !== null)\n .map((item): [string, string] => [item.name, overrides[item.name] ?? inferredValue(item)]);\n\n const known = new Set(inferred.map((item) => item.name));\n const appended = Object.entries(overrides).filter(\n (entry): entry is [string, string] => !known.has(entry[0]) && entry[1] != null,\n );\n\n return [...kept, ...appended];\n }\n\n // inferred header value: the example, the example keys, or a placeholder for the user to fill\n private headerExample(p: ParameterObject): string {\n if (p.example != null) {\n return p.example.toString();\n }\n if (p.examples && Object.keys(p.examples).length > 0) {\n return Object.keys(p.examples).join(',');\n }\n return '<placeholder>';\n }\n\n // a non-exploded array param (`?ids=1,2,3`) needs its values joined: `ids->joinNotNull(\",\")`. see ROADMAP R8\n // exploded arrays (the OAS default) already work as a plain array value\n private arrayJoin(p: Param): string {\n const parameter = p.parameter;\n if (_.get(parameter, 'schema.type') !== 'array' || parameter.explode !== false) {\n return '';\n }\n const delimiter = parameter.style === 'spaceDelimited' ? ' ' : parameter.style === 'pipeDelimited' ? '|' : ',';\n return `->joinNotNull(\"${delimiter}\")`;\n }\n\n private writeSelection(context: OasContext, writer: Writer, type: IType, selection: string[]): void {\n context.indent = 6;\n type.select(context, writer, selection);\n }\n\n // mirrors Body.select formatting, but the user's raw JSONSelection replaces the whole mapping\n private writeBodyOverride(writer: Writer, body: string): void {\n writer.write(',\\n');\n const spacing = ' '.repeat(6);\n writer.write(spacing + 'body: \"\"\"\\n');\n for (const line of body.split('\\n')) {\n writer.write(spacing + ' ' + line + '\\n');\n }\n writer.write(spacing + '\"\"\"\\n' + ' '.repeat(5));\n }\n\n private writeBodySelection(context: OasContext, writer: Writer, body: Body, selection: string[]): void {\n writer.write(',\\n');\n context.indent = 6;\n if (body) {\n body.select(context, writer, selection);\n }\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"operationWriter.js","sourceRoot":"","sources":["../../../src/oas/io/operationWriter.ts"],"names":[],"mappings":"AAAA,OAAO,CAAC,MAAM,QAAQ,CAAC;AAIvB,OAAO,EAA0B,CAAC,EAAE,MAAM,sBAAsB,CAAC;AACjE,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAC5C,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEjD,OAAO,EAAE,4BAA4B,EAAE,cAAc,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAEjH,MAAM,OAAO,eAAe;IAGN;IAFZ,YAAY,CAAe;IAEnC,YAAoB,GAAW;QAAX,QAAG,GAAH,GAAG,CAAQ;QAC7B,IAAI,CAAC,YAAY,GAAG,IAAI,YAAY,CAAC,GAAG,CAAC,CAAC;IAC5C,CAAC;IAEM,UAAU,CAAC,OAAmB,EAAE,MAAc,EAAE,SAA6B,EAAE,SAAmB;QACvG,MAAM,YAAY,GAAG,IAAI,GAAG,CAAS,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAE5E,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;QACzF,IAAI,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC;YAAE,OAAO;QAE7B,MAAM,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC;QAE/B,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,CAAC,CAAC;YACnC,IAAI,CAAC,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,CAAC,CAAC;YACtD,OAAO,CAAC,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACpC,CAAC;QAED,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IACxB,CAAC;IAEM,cAAc,CAAC,OAAmB,EAAE,MAAc,EAAE,SAA6B,EAAE,SAAmB;QAC3G,MAAM,YAAY,GAAG,IAAI,GAAG,CAAS,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAE5E,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;QACzF,IAAI,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC;YAAE,OAAO;QAE7B,MAAM,CAAC,KAAK,CAAC,mBAAmB,CAAC,CAAC;QAElC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,CAAC,CAAC;YACnC,IAAI,CAAC,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,CAAC,CAAC;YACtD,OAAO,CAAC,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACpC,CAAC;QAED,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IACxB,CAAC;IAEM,cAAc,CAAC,OAAmB,EAAE,MAAc,EAAE,IAAW,EAAE,SAAmB;QACzF,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YAClB,MAAM,IAAI,KAAK,CAAC,mCAAmC,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC;QAChE,CAAC;QAED,MAAM,EAAE,GAAG,IAAI,CAAC;QAChB,MAAM,MAAM,GAAG,CAAC,CAAC;QACjB,IAAI,OAAO,GAAG,GAAG,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QACrC,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC;QAE3C,OAAO,GAAG,GAAG,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QACjC,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;QAE9E,IAAI,CAAC,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,SAAS,EAAE,MAAM,CAAC,CAAC;QAE3D,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,kBAAkB,CAAC,CAAC;QAE5D,qFAAqF;QACrF,IAAI,EAAE,CAAC,UAAU,EAAE,CAAC;YAClB,IAAI,CAAC,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,CAAC,UAAU,EAAE,SAAS,CAAC,CAAC;QACjE,CAAC;QAED,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QAErC,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,CAAC,CAAC;QAErD,OAAO,GAAG,GAAG,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QACjC,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IACrC,CAAC;IAEO,aAAa,CAAC,OAAmB,EAAE,MAAc,EAAE,EAAM,EAAE,SAAmB,EAAE,OAAe;QACrG,MAAM,QAAQ,GAAG,OAAO,CAAC,eAAe,CAAC,SAAS,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;QAE5D,wEAAwE;QACxE,MAAM,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC,IAAI,IAAI,CAAC,CAAC,KAAK,CAAC,GAAG,GAAG,IAAI,CAAC,aAAa,CAAC,EAAE,EAAE,QAAQ,CAAC,GAAG,GAAG,CAAC,CAAC;QACnF,wEAAwE;QACxE,MAAM,gBAAgB,GAAG,IAAI,CAAC,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,QAAQ,CAAC,CAAC;QAC9E,MAAM,YAAY,GAAG,IAAI,CAAC,YAAY,CAAC,MAAM,EAAE,EAAE,EAAE,QAAQ,CAAC,CAAC;QAE7D,gFAAgF;QAChF,8EAA8E;QAC9E,IAAI,OAAO,QAAQ,EAAE,IAAI,KAAK,QAAQ,EAAE,CAAC;YACvC,yEAAyE;YACzE,IAAI,CAAC,iBAAiB,CAAC,MAAM,EAAE,QAAQ,CAAC,IAAI,CAAC,CAAC;YAC9C,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACpB,CAAC;aAAM,IAAI,QAAQ,EAAE,IAAI,KAAK,SAAS,IAAI,EAAE,CAAC,IAAI,EAAE,CAAC;YACnD,yEAAyE;YACzE,IAAI,CAAC,kBAAkB,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;YAC7D,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACpB,CAAC;aAAM,IAAI,gBAAgB,IAAI,YAAY,EAAE,CAAC;YAC5C,kFAAkF;YAClF,MAAM,CAAC,KAAK,CAAC,IAAI,GAAG,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC;QAC3C,CAAC;aAAM,CAAC;YACN,yEAAyE;YACzE,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACpB,CAAC;IACH,CAAC;IAED,yGAAyG;IACzG,iGAAiG;IACzF,aAAa,CAAC,EAAM,EAAE,QAA0B;QACtD,OAAO,CAAC,QAAQ,EAAE,IAAI,IAAI,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,cAAc,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE,CAC/E,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,UAAU,MAAM,CAAC,YAAY,CAAC,IAAI,CAAC,GAAG,CAClE,CAAC;IACJ,CAAC;IAED,gGAAgG;IACxF,gBAAgB,CAAC,OAAmB,EAAE,MAAc,EAAE,EAAM,EAAE,QAA0B;QAC9F,gFAAgF;QAChF,4DAA4D;QAC5D,IAAI,WAAW,GAAG,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAQ,EAAE,EAAE;YAC9C,OAAO,CAAC,CAAC,SAAS,CAAC,EAAE,IAAI,CAAC,CAAC,SAAS,CAAC,EAAE,CAAC,WAAW,EAAE,KAAK,OAAO,CAAC;QACpE,CAAC,CAAC,CAAC;QAEH,mDAAmD;QACnD,IAAI,OAAO,CAAC,eAAe,EAAE,gBAAgB,EAAE,CAAC;YAC9C,WAAW,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC,CAAQ,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;QAC7D,CAAC;QAED,uFAAuF;QACvF,MAAM,OAAO,GAAG,IAAI,CAAC,cAAc,CACjC,WAAW,EACX,QAAQ,EAAE,WAAW,IAAI,EAAE,EAC3B,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAC5D,CAAC;QACF,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACzB,OAAO,KAAK,CAAC;QACf,CAAC;QAED,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACnB,IAAI,OAAO,GAAG,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;QAE5B,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,oBAAoB,CAAC,CAAC;QAClD,OAAO,GAAG,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;QACxB,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;QACzC,OAAO,GAAG,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QACzB,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,OAAO,EAAE,CAAC;YACnC,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,IAAI,GAAG,MAAM,KAAK,IAAI,CAAC,CAAC;QACtD,CAAC;QACD,OAAO,GAAG,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;QACxB,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QACnC,OAAO,GAAG,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;QACxB,4DAA4D;QAC5D,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QACnC,OAAO,IAAI,CAAC;IACd,CAAC;IAED,4FAA4F;IACpF,YAAY,CAAC,MAAc,EAAE,EAAM,EAAE,QAA0B;QACrE,mFAAmF;QACnF,MAAM,OAAO,GAAG,EAAE,CAAC,SAAS,CAAC,aAAa,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,EAAE,CAAC,WAAW,EAAE,KAAK,QAAQ,CAAC,CAAC;QACpG,IAAI,OAAO,GAAG,IAAI,CAAC,cAAc,CAAC,OAAO,EAAE,QAAQ,EAAE,OAAO,IAAI,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC;QAElG,iGAAiG;QACjG,2FAA2F;QAC3F,8FAA8F;QAC9F,wEAAwE;QACxE,MAAM,IAAI,GAAG,IAAI,CAAC,eAAe,CAAC,EAAE,CAAC,CAAC;QACtC,IAAI,IAAI,EAAE,CAAC;YACT,MAAM,QAAQ,GAAG,CAAC,IAAY,EAAE,EAAE,CAAC,IAAI,CAAC,WAAW,EAAE,KAAK,IAAI,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;YAChF,MAAM,UAAU,GAAG,MAAM,CAAC,IAAI,CAAC,QAAQ,EAAE,OAAO,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YACvE,IAAI,CAAC,UAAU,EAAE,CAAC;gBAChB,OAAO,GAAG,CAAC,IAAI,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YACnE,CAAC;QACH,CAAC;QAED,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACzB,OAAO,KAAK,CAAC;QACf,CAAC;QAED,IAAI,OAAO,GAAG,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;QAC5B,8FAA8F;QAC9F,MAAM,CAAC,KAAK,CAAC,IAAI,GAAG,OAAO,GAAG,cAAc,CAAC,CAAC;QAC9C,OAAO,GAAG,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;QAExB,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,OAAO,EAAE,CAAC;YACnC,MAAM,CAAC,KAAK,CAAC,OAAO,GAAG,YAAY,GAAG,cAAc,KAAK,OAAO,CAAC,CAAC;QACpE,CAAC;QAED,OAAO,GAAG,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;QACxB,4DAA4D;QAC5D,MAAM,CAAC,KAAK,CAAC,OAAO,GAAG,GAAG,CAAC,CAAC;QAC5B,OAAO,IAAI,CAAC;IACd,CAAC;IAED,wFAAwF;IACxF,EAAE;IACF,6FAA6F;IAC7F,yFAAyF;IACzF,0FAA0F;IAC1F,oGAAoG;IACpG,6FAA6F;IAC7F,+EAA+E;IACvE,eAAe,CAAC,EAAM;QAC5B,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC;QAC5B,sFAAsF;QACtF,IAAI,CAAC,4BAA4B,CAAC,GAAG,CAAC,EAAE,CAAC;YACvC,OAAO,IAAI,CAAC;QACd,CAAC;QAED,4EAA4E;QAC5E,MAAM,GAAG,GAAG,EAAE,CAAC,SAAS,CAAC,MAAM,CAAC,QAAQ,CAAC;QACzC,4EAA4E;QAC5E,MAAM,SAAS,GAAG,GAAG,IAAI,cAAc,CAAC,GAAG,CAAC,CAAC;QAE7C,wDAAwD;QACxD,MAAM,OAAO,GAAG,eAAe,CAAC,GAAG,CAAC,CAAC;QACrC,uFAAuF;QACvF,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,iBAAiB,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;QACnE,6DAA6D;QAC7D,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;YACzB,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,eAAe,EAAE,CAAC,IAAI,IAAI,EAAE,CAAC,SAAS,CAAC,IAAI,GAAG,CAAC,CAAC;QACnE,CAAC;QACD,OAAO,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IACrD,CAAC;IAED,uFAAuF;IACvF,mEAAmE;IAC3D,cAAc,CACpB,QAAa,EACb,SAAwC,EACxC,aAAkC;QAElC,MAAM,IAAI,GAAG,QAAQ;aAClB,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,IAAI,CAAC;aAC/C,GAAG,CAAC,CAAC,IAAI,EAAoB,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAE7F,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;QACzD,MAAM,QAAQ,GAAG,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,MAAM,CAC/C,CAAC,KAAK,EAA6B,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,IAAI,IAAI,CAC/E,CAAC;QAEF,OAAO,CAAC,GAAG,IAAI,EAAE,GAAG,QAAQ,CAAC,CAAC;IAChC,CAAC;IAED,8FAA8F;IACtF,aAAa,CAAC,CAAkB;QACtC,IAAI,CAAC,CAAC,OAAO,IAAI,IAAI,EAAE,CAAC;YACtB,OAAO,CAAC,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC;QAC9B,CAAC;QACD,IAAI,CAAC,CAAC,QAAQ,IAAI,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACrD,OAAO,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC3C,CAAC;QACD,OAAO,eAAe,CAAC;IACzB,CAAC;IAED,6GAA6G;IAC7G,wEAAwE;IAChE,SAAS,CAAC,CAAQ;QACxB,MAAM,SAAS,GAAG,CAAC,CAAC,SAAS,CAAC;QAC9B,IAAI,CAAC,CAAC,GAAG,CAAC,SAAS,EAAE,aAAa,CAAC,KAAK,OAAO,IAAI,SAAS,CAAC,OAAO,KAAK,KAAK,EAAE,CAAC;YAC/E,OAAO,EAAE,CAAC;QACZ,CAAC;QACD,MAAM,SAAS,GAAG,SAAS,CAAC,KAAK,KAAK,gBAAgB,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC,KAAK,KAAK,eAAe,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC;QAC/G,OAAO,kBAAkB,SAAS,IAAI,CAAC;IACzC,CAAC;IAEO,cAAc,CAAC,OAAmB,EAAE,MAAc,EAAE,IAAW,EAAE,SAAmB;QAC1F,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC;QACnB,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,CAAC,CAAC;IAC1C,CAAC;IAED,8FAA8F;IACtF,iBAAiB,CAAC,MAAc,EAAE,IAAY;QACpD,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QACpB,MAAM,OAAO,GAAG,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;QAC9B,MAAM,CAAC,KAAK,CAAC,OAAO,GAAG,aAAa,CAAC,CAAC;QACtC,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;YACpC,MAAM,CAAC,KAAK,CAAC,OAAO,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC,CAAC;QAC7C,CAAC;QACD,MAAM,CAAC,KAAK,CAAC,OAAO,GAAG,OAAO,GAAG,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;IAClD,CAAC;IAEO,kBAAkB,CAAC,OAAmB,EAAE,MAAc,EAAE,IAAU,EAAE,SAAmB;QAC7F,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QACpB,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC;QACnB,IAAI,IAAI,EAAE,CAAC;YACT,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,CAAC,CAAC;QAC1C,CAAC;IACH,CAAC;CACF","sourcesContent":["import _ from 'lodash';\nimport { ParameterObject } from 'oas/types';\nimport { OasContext, RequestOverride } from '../oasContext.js';\nimport { OasGen } from '../oasGen.js';\nimport { Body, IType, Op, Param, T } from '../nodes/internal.js';\nimport { Naming } from '../utils/naming.js';\nimport { ErrorsWriter } from './errorsWriter.js';\nimport { Writer } from './writer.js';\nimport { anyOperationDeclaresSecurity, globalSecurity, securitySchemes, resolveAuthHeader } from './security.js';\n\nexport class OperationWriter {\n private errorsWriter: ErrorsWriter;\n\n constructor(private gen: OasGen) {\n this.errorsWriter = new ErrorsWriter(gen);\n }\n\n public writeQuery(context: OasContext, writer: Writer, collected: Map<string, IType>, selection: string[]): void {\n const selectionSet = new Set<string>(selection.map((s) => s.split('>')[0]));\n\n const paths = Array.from(collected.values()).filter((path) => selectionSet.has(path.id));\n if (_.isEmpty(paths)) return;\n\n writer.write('type Query {\\n');\n\n for (const path of paths) {\n path.generate(context, writer, []);\n this.writeConnector(context, writer, path, selection);\n context.generatedSet.add(path.id);\n }\n\n writer.write('}\\n\\n');\n }\n\n public writeMutations(context: OasContext, writer: Writer, collected: Map<string, IType>, selection: string[]): void {\n const selectionSet = new Set<string>(selection.map((s) => s.split('>')[0]));\n\n const paths = Array.from(collected.values()).filter((path) => selectionSet.has(path.id));\n if (_.isEmpty(paths)) return;\n\n writer.write('type Mutation {\\n');\n\n for (const path of paths) {\n path.generate(context, writer, []);\n this.writeConnector(context, writer, path, selection);\n context.generatedSet.add(path.id);\n }\n\n writer.write('}\\n\\n');\n }\n\n public writeConnector(context: OasContext, writer: Writer, type: IType, selection: string[]): void {\n if (!T.isOp(type)) {\n throw new Error(`expected an operation node, got ${type.id}`);\n }\n\n const op = type;\n const indent = 0;\n let spacing = ' '.repeat(indent + 4);\n writer.write(spacing).write('@connect(\\n');\n\n spacing = ' '.repeat(indent + 6);\n writer.write(spacing).write('source: \"api\"\\n').write(spacing).write('http: ');\n\n this.requestMethod(context, writer, op, selection, indent);\n\n writer.write('\\n').write(spacing).write('selection: \"\"\"\\n');\n\n // truthiness, not _.has — the declared-but-unset field is still an own property. #33\n if (op.resultType) {\n this.writeSelection(context, writer, op.resultType, selection);\n }\n\n writer.write(spacing).write('\"\"\"\\n');\n\n this.errorsWriter.write(context, writer, op, indent);\n\n spacing = ' '.repeat(indent + 4);\n writer.write(spacing).write(')\\n');\n }\n\n private requestMethod(context: OasContext, writer: Writer, op: Op, selection: string[], _indent: number): void {\n const override = context.generateOptions.overrides?.[op.id];\n\n // the verb + path first, then query params, headers and lastly the body\n writer.write(`{ ${op.verb}: `).write('\"' + this.templatedPath(op, override) + '\"');\n // each block starts on its own line and ends without a trailing newline\n const wroteQueryParams = this.writeQueryParams(context, writer, op, override);\n const wroteHeaders = this.writeHeaders(writer, op, override);\n\n // body (POST, PUT, etc.): an override (raw JSONSelection) replaces the inferred\n // `$args.input { … }` mapping; null drops the body altogether. see ROADMAP R9\n if (typeof override?.body === 'string') {\n // body emits its own trailing indent, so close the object right after it\n this.writeBodyOverride(writer, override.body);\n writer.write('}');\n } else if (override?.body === undefined && op.body) {\n // body emits its own trailing indent, so close the object right after it\n this.writeBodySelection(context, writer, op.body, selection);\n writer.write('}');\n } else if (wroteQueryParams || wroteHeaders) {\n // no body: close on its own line, aligned under `http:` (avoids a column-0 brace)\n writer.write('\\n' + ' '.repeat(6) + '}');\n } else {\n // nothing but the path: keep the compact single-line form `{ GET: \"/x\"}`\n writer.write('}');\n }\n }\n\n // template each {elem} as {$args.<sanitised>} (the arg name), not the raw OAS key. see docs/issues.md #2\n // an override path may already template (`{$args.id}`, `{$config.v}`) — leave `$` segments alone\n private templatedPath(op: Op, override?: RequestOverride): string {\n return (override?.path ?? op.operation.path).replace(/\\{([^}]+)\\}/g, (m, name) =>\n name.startsWith('$') ? m : `{$args.${Naming.genParamName(name)}}`,\n );\n }\n\n // returns true when a queryParams block was written (so the caller can place the closing brace)\n private writeQueryParams(context: OasContext, writer: Writer, op: Op, override?: RequestOverride): boolean {\n // we now include all query params, not just required ones. if they are not set,\n // then the connectors will not include them in the request.\n let queryParams = op.params.filter((p: Param) => {\n return p.parameter.in && p.parameter.in.toLowerCase() === 'query';\n });\n\n // Skip optional params if skipOptionalArgs is true\n if (context.generateOptions?.skipOptionalArgs) {\n queryParams = queryParams.filter((p: Param) => p.required);\n }\n\n // e.g. `\"api-version\": $('2024-01')` appended, `\"ids\": ids->joinNotNull(\";\")` replaced\n const entries = this.mergeOverrides(\n queryParams,\n override?.queryParams ?? {},\n (p) => `${Naming.genParamName(p.name)}${this.arrayJoin(p)}`,\n );\n if (entries.length === 0) {\n return false;\n }\n\n writer.write('\\n');\n let spacing = ' '.repeat(6);\n\n writer.write(spacing).write(`queryParams: \"\"\"\\n`);\n spacing = ' '.repeat(8);\n writer.write(spacing).write(`$args {\\n`);\n spacing = ' '.repeat(10);\n for (const [key, value] of entries) {\n writer.write(spacing).write(`\"${key}\": ${value}\\n`);\n }\n spacing = ' '.repeat(8);\n writer.write(spacing).write('}\\n');\n spacing = ' '.repeat(6);\n // no trailing newline — the caller adds the separator/brace\n writer.write(spacing).write('\"\"\"');\n return true;\n }\n\n // returns true when a headers block was written (so the caller can place the closing brace)\n private writeHeaders(writer: Writer, op: Op, override?: RequestOverride): boolean {\n // OAS `header` params, with user overrides merged in (string replaces, null drops)\n const headers = op.operation.getParameters().filter((p) => p.in && p.in.toLowerCase() === 'header');\n let entries = this.mergeOverrides(headers, override?.headers ?? {}, (p) => this.headerExample(p));\n\n // R5 slice 2: add this operation's effective auth header (only when the spec is in per-op mode).\n // HTTP header names are case-insensitive: an explicit user override of the same name wins;\n // otherwise the resolved auth replaces any inferred OAS header of that name (so we never emit\n // a real credential alongside a placeholder that differs only in case).\n const auth = this.authHeaderEntry(op);\n if (auth) {\n const sameName = (name: string) => name.toLowerCase() === auth[0].toLowerCase();\n const overridden = Object.keys(override?.headers ?? {}).some(sameName);\n if (!overridden) {\n entries = [auth, ...entries.filter(([name]) => !sameName(name))];\n }\n }\n\n if (entries.length === 0) {\n return false;\n }\n\n let spacing = ' '.repeat(6);\n // leading newline so the block starts on its own line (after the path or a queryParams block)\n writer.write('\\n' + spacing + 'headers: [\\n');\n spacing = ' '.repeat(8);\n\n for (const [key, value] of entries) {\n writer.write(spacing + `{ name: \"${key}\", value: \"${value}\" }\\n`);\n }\n\n spacing = ' '.repeat(6);\n // no trailing newline — the caller adds the separator/brace\n writer.write(spacing + ']');\n return true;\n }\n\n // R5 slice 2: this operation's effective auth header as a [name, value] entry, or null.\n //\n // Emits only in *per-op mode* — when some operation in the spec declares its own `security`,\n // the shared @source auth header is suppressed, so each @connect must carry its own. The\n // effective requirement is the op's own `security` when present, else the global default:\n // security: [{ AdminBearer: [] }] (own) -> that scheme's header (e.g. Authorization: Bearer)\n // security: [] (public) -> no header (the op correctly sends nothing)\n // no own `security` -> the inherited global header\n private authHeaderEntry(op: Op): [string, string] | null {\n const api = this.gen.parser;\n // uniform mode: @source already carries the shared auth, nothing to add per operation\n if (!anyOperationDeclaresSecurity(api)) {\n return null;\n }\n\n // the op's own requirement; `undefined` = inherit the global, `[]` = public\n const own = op.operation.schema.security;\n // `??` not `||`: an explicit `[]` (public) must NOT fall back to the global\n const effective = own ?? globalSecurity(api);\n\n // read the scheme definitions the requirement refers to\n const schemes = securitySchemes(api);\n // resolve the effective requirement to one header, collecting per-scheme drop warnings\n const { header, warnings } = resolveAuthHeader(effective, schemes);\n // surface dropped schemes loudly, tagged with this operation\n for (const w of warnings) {\n console.warn(`${w} (operation ${op.verb} ${op.operation.path})`);\n }\n return header ? [header.name, header.value] : null;\n }\n\n // merge user overrides over the inferred params: a string replaces the inferred value,\n // null drops the entry, an unknown key is appended. see ROADMAP R8\n private mergeOverrides<T extends { name: string }>(\n inferred: T[],\n overrides: Record<string, string | null>,\n inferredValue: (item: T) => string,\n ): Array<[string, string]> {\n const kept = inferred\n .filter((item) => overrides[item.name] !== null)\n .map((item): [string, string] => [item.name, overrides[item.name] ?? inferredValue(item)]);\n\n const known = new Set(inferred.map((item) => item.name));\n const appended = Object.entries(overrides).filter(\n (entry): entry is [string, string] => !known.has(entry[0]) && entry[1] != null,\n );\n\n return [...kept, ...appended];\n }\n\n // inferred header value: the example, the example keys, or a placeholder for the user to fill\n private headerExample(p: ParameterObject): string {\n if (p.example != null) {\n return p.example.toString();\n }\n if (p.examples && Object.keys(p.examples).length > 0) {\n return Object.keys(p.examples).join(',');\n }\n return '<placeholder>';\n }\n\n // a non-exploded array param (`?ids=1,2,3`) needs its values joined: `ids->joinNotNull(\",\")`. see ROADMAP R8\n // exploded arrays (the OAS default) already work as a plain array value\n private arrayJoin(p: Param): string {\n const parameter = p.parameter;\n if (_.get(parameter, 'schema.type') !== 'array' || parameter.explode !== false) {\n return '';\n }\n const delimiter = parameter.style === 'spaceDelimited' ? ' ' : parameter.style === 'pipeDelimited' ? '|' : ',';\n return `->joinNotNull(\"${delimiter}\")`;\n }\n\n private writeSelection(context: OasContext, writer: Writer, type: IType, selection: string[]): void {\n context.indent = 6;\n type.select(context, writer, selection);\n }\n\n // mirrors Body.select formatting, but the user's raw JSONSelection replaces the whole mapping\n private writeBodyOverride(writer: Writer, body: string): void {\n writer.write(',\\n');\n const spacing = ' '.repeat(6);\n writer.write(spacing + 'body: \"\"\"\\n');\n for (const line of body.split('\\n')) {\n writer.write(spacing + ' ' + line + '\\n');\n }\n writer.write(spacing + '\"\"\"\\n' + ' '.repeat(5));\n }\n\n private writeBodySelection(context: OasContext, writer: Writer, body: Body, selection: string[]): void {\n writer.write(',\\n');\n context.indent = 6;\n if (body) {\n body.select(context, writer, selection);\n }\n }\n}\n"]}
|
|
@@ -6,17 +6,15 @@ export declare class SchemaWriter {
|
|
|
6
6
|
writeJSONScalar(writer: Writer): void;
|
|
7
7
|
writeDirectives(writer: Writer): void;
|
|
8
8
|
/**
|
|
9
|
-
* R5
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
9
|
+
* R5: derive an `@source`-level auth header from the spec's *global* security requirement.
|
|
10
|
+
* Returns the header literal (e.g. `{ name: "Authorization", value: "Bearer {$config.token}" }`)
|
|
11
|
+
* or null when no header should be emitted on `@source`.
|
|
12
|
+
*
|
|
13
|
+
* Mode switch (slice 2): if any operation declares its own `security`, auth moves to each
|
|
14
|
+
* operation's `@connect` (see operationWriter) and nothing is emitted here — a shared `@source`
|
|
15
|
+
* header is unsafe then (a public op would still send it; a different-named override would send
|
|
16
|
+
* both). Otherwise (uniform mode, slice 1) the global requirement is emitted once on `@source`.
|
|
13
17
|
*/
|
|
14
18
|
private securityHeader;
|
|
15
|
-
/** Map a single security scheme to its `@source` header literal, or null when it produces no header. */
|
|
16
|
-
private mapScheme;
|
|
17
|
-
/** Build a loud warning for a scheme that was not emitted on `@source`. */
|
|
18
|
-
private dropWarning;
|
|
19
|
-
/** Returns "METHOD /path" for every operation that declares its own `security` (including `security: []`). */
|
|
20
|
-
private operationsWithSecurity;
|
|
21
19
|
private getServerUrl;
|
|
22
20
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { DEFAULT_VERSIONS } from '../../versions.js';
|
|
2
|
+
import { anyOperationDeclaresSecurity, globalSecurity, securitySchemes, resolveAuthHeader } from './security.js';
|
|
2
3
|
export class SchemaWriter {
|
|
3
4
|
gen;
|
|
4
5
|
constructor(gen) {
|
|
@@ -29,117 +30,42 @@ export class SchemaWriter {
|
|
|
29
30
|
}
|
|
30
31
|
}
|
|
31
32
|
/**
|
|
32
|
-
* R5
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
33
|
+
* R5: derive an `@source`-level auth header from the spec's *global* security requirement.
|
|
34
|
+
* Returns the header literal (e.g. `{ name: "Authorization", value: "Bearer {$config.token}" }`)
|
|
35
|
+
* or null when no header should be emitted on `@source`.
|
|
36
|
+
*
|
|
37
|
+
* Mode switch (slice 2): if any operation declares its own `security`, auth moves to each
|
|
38
|
+
* operation's `@connect` (see operationWriter) and nothing is emitted here — a shared `@source`
|
|
39
|
+
* header is unsafe then (a public op would still send it; a different-named override would send
|
|
40
|
+
* both). Otherwise (uniform mode, slice 1) the global requirement is emitted once on `@source`.
|
|
36
41
|
*/
|
|
37
42
|
securityHeader(api) {
|
|
38
|
-
//
|
|
39
|
-
|
|
40
|
-
const global = def.security;
|
|
41
|
-
// OAS3 components.securitySchemes, falling back to Swagger 2.0 securityDefinitions
|
|
42
|
-
// (oas-normalize usually up-converts, but keep the fallback for safety).
|
|
43
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
44
|
-
const schemes = (def.components?.securitySchemes ?? def.securityDefinitions ?? {});
|
|
45
|
-
// Concern-2 guard: a per-operation `security` (including anonymous `security: []`)
|
|
46
|
-
// *replaces* the global requirement, so the requirement is not uniform across
|
|
47
|
-
// endpoints. A shared @source header would wrongly attach auth to overriding
|
|
48
|
-
// endpoints — emit nothing and warn per affected operation. Checked before the
|
|
49
|
-
// global guard so per-op-only specs (e.g. petstore) still warn loudly.
|
|
50
|
-
const overriding = this.operationsWithSecurity(api);
|
|
51
|
-
if (overriding.length > 0) {
|
|
52
|
-
for (const op of overriding) {
|
|
53
|
-
console.warn(`[security] operation ${op} declares its own \`security\`; not emitting a global @source auth header (per-operation auth deferred to a future slice).`);
|
|
54
|
-
}
|
|
43
|
+
// per-op mode: some operation overrides the global → let each @connect carry its own auth
|
|
44
|
+
if (anyOperationDeclaresSecurity(api)) {
|
|
55
45
|
return null;
|
|
56
46
|
}
|
|
57
|
-
//
|
|
47
|
+
// read the document-level (global) security requirement
|
|
48
|
+
const global = globalSecurity(api);
|
|
49
|
+
// no global requirement → keep the headerless @source byte-for-byte
|
|
58
50
|
if (!global || global.length === 0) {
|
|
59
51
|
return null;
|
|
60
52
|
}
|
|
61
|
-
//
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
}
|
|
69
|
-
// Pick the first scheme that maps to a header; warn about every other scheme.
|
|
70
|
-
let header = null;
|
|
71
|
-
for (const name of names) {
|
|
72
|
-
const scheme = schemes[name];
|
|
73
|
-
const mapped = scheme ? this.mapScheme(scheme) : null;
|
|
74
|
-
if (mapped && !header) {
|
|
75
|
-
header = mapped;
|
|
76
|
-
}
|
|
77
|
-
else {
|
|
78
|
-
console.warn(this.dropWarning(name, scheme));
|
|
79
|
-
}
|
|
53
|
+
// read the scheme definitions (apiKey/http/oauth2/...) the requirement refers to
|
|
54
|
+
const schemes = securitySchemes(api);
|
|
55
|
+
// resolve the global requirement to one header, collecting per-scheme drop warnings
|
|
56
|
+
const { header, warnings } = resolveAuthHeader(global, schemes);
|
|
57
|
+
// surface every dropped scheme loudly (never silently)
|
|
58
|
+
for (const w of warnings) {
|
|
59
|
+
console.warn(w);
|
|
80
60
|
}
|
|
81
61
|
if (!header) {
|
|
62
|
+
// flatten the referenced scheme names for the summary message
|
|
63
|
+
const names = global.flatMap((req) => Object.keys(req));
|
|
82
64
|
console.warn(`[security] global security requirement (${names.join(', ')}) maps to no header-producing scheme; no auth header emitted on @source.`);
|
|
83
65
|
return null;
|
|
84
66
|
}
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
/** Map a single security scheme to its `@source` header literal, or null when it produces no header. */
|
|
88
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
89
|
-
mapScheme(scheme) {
|
|
90
|
-
switch (scheme.type) {
|
|
91
|
-
case 'apiKey':
|
|
92
|
-
if (scheme.in === 'header' && scheme.name) {
|
|
93
|
-
return `{ name: "${scheme.name}", value: "{$config.apiKey}" }`;
|
|
94
|
-
}
|
|
95
|
-
return null; // query / cookie handled via warnings
|
|
96
|
-
case 'http':
|
|
97
|
-
if (scheme.scheme === 'bearer') {
|
|
98
|
-
return `{ name: "Authorization", value: "Bearer {$config.token}" }`;
|
|
99
|
-
}
|
|
100
|
-
if (scheme.scheme === 'basic') {
|
|
101
|
-
return `{ name: "Authorization", value: "Basic {$config.token}" }`;
|
|
102
|
-
}
|
|
103
|
-
return null;
|
|
104
|
-
case 'oauth2':
|
|
105
|
-
case 'openIdConnect':
|
|
106
|
-
return `{ name: "Authorization", value: "Bearer {$config.token}" }`;
|
|
107
|
-
default:
|
|
108
|
-
return null;
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
/** Build a loud warning for a scheme that was not emitted on `@source`. */
|
|
112
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
113
|
-
dropWarning(name, scheme) {
|
|
114
|
-
if (!scheme) {
|
|
115
|
-
return `[security] scheme "${name}" is referenced by global security but not defined; skipped.`;
|
|
116
|
-
}
|
|
117
|
-
if (scheme.type === 'apiKey' && scheme.in === 'query') {
|
|
118
|
-
return `[security] scheme "${name}" is apiKey in query — not emitted as a header (query-param auth deferred).`;
|
|
119
|
-
}
|
|
120
|
-
if (scheme.type === 'apiKey' && scheme.in === 'cookie') {
|
|
121
|
-
return `[security] scheme "${name}" is apiKey in cookie — not emitted as a header (cookie auth deferred).`;
|
|
122
|
-
}
|
|
123
|
-
return `[security] scheme "${name}" (${scheme.type}) not emitted — only the first header-producing scheme of the global requirement is mapped on @source.`;
|
|
124
|
-
}
|
|
125
|
-
/** Returns "METHOD /path" for every operation that declares its own `security` (including `security: []`). */
|
|
126
|
-
operationsWithSecurity(api) {
|
|
127
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
128
|
-
const paths = api.getDefinition().paths ?? {};
|
|
129
|
-
const methods = ['get', 'put', 'post', 'delete', 'options', 'head', 'patch', 'trace'];
|
|
130
|
-
const result = [];
|
|
131
|
-
for (const p of Object.keys(paths)) {
|
|
132
|
-
const item = paths[p];
|
|
133
|
-
if (!item)
|
|
134
|
-
continue;
|
|
135
|
-
for (const m of methods) {
|
|
136
|
-
const op = item[m];
|
|
137
|
-
if (op && Object.prototype.hasOwnProperty.call(op, 'security')) {
|
|
138
|
-
result.push(`${m.toUpperCase()} ${p}`);
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
return result;
|
|
67
|
+
// format the resolved header into the @source literal
|
|
68
|
+
return `{ name: "${header.name}", value: "${header.value}" }`;
|
|
143
69
|
}
|
|
144
70
|
getServerUrl(server) {
|
|
145
71
|
if (!server) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schemaWriter.js","sourceRoot":"","sources":["../../../src/oas/io/schemaWriter.ts"],"names":[],"mappings":"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","sourcesContent":["import Oas from 'oas';\nimport { ServerObject } from 'oas/types';\nimport { DEFAULT_VERSIONS } from '../../versions.js';\nimport { OasGen } from '../oasGen.js';\nimport { Writer } from './writer.js';\n\nexport class SchemaWriter {\n constructor(private gen: OasGen) {}\n\n public writeJSONScalar(writer: Writer): void {\n writer.write('\\nscalar JSON\\n\\n');\n }\n\n public writeDirectives(writer: Writer): void {\n const api: Oas = this.gen.parser;\n // a spec's servers[0] can be stale or wrong (petstore) — an explicit baseURL wins\n const host = this.gen.options.baseURL ?? this.getServerUrl(api.getDefinition().servers?.[0]);\n const federationVersion = this.gen.options.federationVersion || DEFAULT_VERSIONS.federationVersion;\n const connectorSpecVersion = this.gen.options.connectorSpecVersion || DEFAULT_VERSIONS.connectorSpecVersion;\n const authHeader = this.securityHeader(api);\n writer\n .write('extend schema\\n')\n .write(` @link(url: \"https://specs.apollo.dev/federation/${federationVersion}\", import: [\"@key\"])\\n`)\n .write(' @link(\\n')\n .write(` url: \"https://specs.apollo.dev/connect/${connectorSpecVersion}\"\\n`)\n .write(' import: [\"@connect\", \"@source\"]\\n')\n .write(' )\\n');\n\n if (authHeader) {\n writer.write(` @source(name: \"api\", http: { baseURL: \"${host}\", headers: [${authHeader}] })\\n\\n`);\n } else {\n writer.write(' @source(name: \"api\", http: { baseURL: \"').write(host).write('\" })\\n\\n');\n }\n }\n\n /**\n * R5 (slice 1): derive an `@source`-level auth header from the spec's *global*\n * security requirement. Returns the header literal (e.g.\n * `{ name: \"Authorization\", value: \"Bearer {$config.token}\" }`) or null when no\n * header should be emitted. Everything deferred is warned about, never silently dropped.\n */\n private securityHeader(api: Oas): string | null {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const def = api.getDefinition() as any;\n const global = def.security as Array<Record<string, string[]>> | undefined;\n // OAS3 components.securitySchemes, falling back to Swagger 2.0 securityDefinitions\n // (oas-normalize usually up-converts, but keep the fallback for safety).\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const schemes = (def.components?.securitySchemes ?? def.securityDefinitions ?? {}) as Record<string, any>;\n\n // Concern-2 guard: a per-operation `security` (including anonymous `security: []`)\n // *replaces* the global requirement, so the requirement is not uniform across\n // endpoints. A shared @source header would wrongly attach auth to overriding\n // endpoints — emit nothing and warn per affected operation. Checked before the\n // global guard so per-op-only specs (e.g. petstore) still warn loudly.\n const overriding = this.operationsWithSecurity(api);\n if (overriding.length > 0) {\n for (const op of overriding) {\n console.warn(\n `[security] operation ${op} declares its own \\`security\\`; not emitting a global @source auth header (per-operation auth deferred to a future slice).`,\n );\n }\n return null;\n }\n\n // No global requirement → keep the current headerless @source byte-for-byte.\n if (!global || global.length === 0) {\n return null;\n }\n\n // Flatten the global requirement to referenced scheme names, in order: across\n // requirement objects (logical OR) and within each object (logical AND).\n const names: string[] = [];\n for (const req of global) {\n for (const name of Object.keys(req)) {\n names.push(name);\n }\n }\n\n // Pick the first scheme that maps to a header; warn about every other scheme.\n let header: string | null = null;\n for (const name of names) {\n const scheme = schemes[name];\n const mapped = scheme ? this.mapScheme(scheme) : null;\n if (mapped && !header) {\n header = mapped;\n } else {\n console.warn(this.dropWarning(name, scheme));\n }\n }\n\n if (!header) {\n console.warn(\n `[security] global security requirement (${names.join(', ')}) maps to no header-producing scheme; no auth header emitted on @source.`,\n );\n return null;\n }\n\n return header;\n }\n\n /** Map a single security scheme to its `@source` header literal, or null when it produces no header. */\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n private mapScheme(scheme: any): string | null {\n switch (scheme.type) {\n case 'apiKey':\n if (scheme.in === 'header' && scheme.name) {\n return `{ name: \"${scheme.name}\", value: \"{$config.apiKey}\" }`;\n }\n return null; // query / cookie handled via warnings\n case 'http':\n if (scheme.scheme === 'bearer') {\n return `{ name: \"Authorization\", value: \"Bearer {$config.token}\" }`;\n }\n if (scheme.scheme === 'basic') {\n return `{ name: \"Authorization\", value: \"Basic {$config.token}\" }`;\n }\n return null;\n case 'oauth2':\n case 'openIdConnect':\n return `{ name: \"Authorization\", value: \"Bearer {$config.token}\" }`;\n default:\n return null;\n }\n }\n\n /** Build a loud warning for a scheme that was not emitted on `@source`. */\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n private dropWarning(name: string, scheme: any): string {\n if (!scheme) {\n return `[security] scheme \"${name}\" is referenced by global security but not defined; skipped.`;\n }\n if (scheme.type === 'apiKey' && scheme.in === 'query') {\n return `[security] scheme \"${name}\" is apiKey in query — not emitted as a header (query-param auth deferred).`;\n }\n if (scheme.type === 'apiKey' && scheme.in === 'cookie') {\n return `[security] scheme \"${name}\" is apiKey in cookie — not emitted as a header (cookie auth deferred).`;\n }\n return `[security] scheme \"${name}\" (${scheme.type}) not emitted — only the first header-producing scheme of the global requirement is mapped on @source.`;\n }\n\n /** Returns \"METHOD /path\" for every operation that declares its own `security` (including `security: []`). */\n private operationsWithSecurity(api: Oas): string[] {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const paths = (api.getDefinition() as any).paths ?? {};\n const methods = ['get', 'put', 'post', 'delete', 'options', 'head', 'patch', 'trace'];\n const result: string[] = [];\n for (const p of Object.keys(paths)) {\n const item = paths[p];\n if (!item) continue;\n for (const m of methods) {\n const op = item[m];\n if (op && Object.prototype.hasOwnProperty.call(op, 'security')) {\n result.push(`${m.toUpperCase()} ${p}`);\n }\n }\n }\n return result;\n }\n\n private getServerUrl(server: ServerObject | undefined): string {\n if (!server) {\n return 'http://localhost:4010';\n }\n let url: string = server.url;\n if (server.variables) {\n for (const key in server.variables) {\n url = url.replace('{' + key + '}', server.variables[key].default);\n }\n }\n return url;\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"schemaWriter.js","sourceRoot":"","sources":["../../../src/oas/io/schemaWriter.ts"],"names":[],"mappings":"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","sourcesContent":["import Oas from 'oas';\nimport { ServerObject } from 'oas/types';\nimport { DEFAULT_VERSIONS } from '../../versions.js';\nimport { OasGen } from '../oasGen.js';\nimport { Writer } from './writer.js';\nimport { anyOperationDeclaresSecurity, globalSecurity, securitySchemes, resolveAuthHeader } from './security.js';\n\nexport class SchemaWriter {\n constructor(private gen: OasGen) {}\n\n public writeJSONScalar(writer: Writer): void {\n writer.write('\\nscalar JSON\\n\\n');\n }\n\n public writeDirectives(writer: Writer): void {\n const api: Oas = this.gen.parser;\n // a spec's servers[0] can be stale or wrong (petstore) — an explicit baseURL wins\n const host = this.gen.options.baseURL ?? this.getServerUrl(api.getDefinition().servers?.[0]);\n const federationVersion = this.gen.options.federationVersion || DEFAULT_VERSIONS.federationVersion;\n const connectorSpecVersion = this.gen.options.connectorSpecVersion || DEFAULT_VERSIONS.connectorSpecVersion;\n const authHeader = this.securityHeader(api);\n writer\n .write('extend schema\\n')\n .write(` @link(url: \"https://specs.apollo.dev/federation/${federationVersion}\", import: [\"@key\"])\\n`)\n .write(' @link(\\n')\n .write(` url: \"https://specs.apollo.dev/connect/${connectorSpecVersion}\"\\n`)\n .write(' import: [\"@connect\", \"@source\"]\\n')\n .write(' )\\n');\n\n if (authHeader) {\n writer.write(` @source(name: \"api\", http: { baseURL: \"${host}\", headers: [${authHeader}] })\\n\\n`);\n } else {\n writer.write(' @source(name: \"api\", http: { baseURL: \"').write(host).write('\" })\\n\\n');\n }\n }\n\n /**\n * R5: derive an `@source`-level auth header from the spec's *global* security requirement.\n * Returns the header literal (e.g. `{ name: \"Authorization\", value: \"Bearer {$config.token}\" }`)\n * or null when no header should be emitted on `@source`.\n *\n * Mode switch (slice 2): if any operation declares its own `security`, auth moves to each\n * operation's `@connect` (see operationWriter) and nothing is emitted here — a shared `@source`\n * header is unsafe then (a public op would still send it; a different-named override would send\n * both). Otherwise (uniform mode, slice 1) the global requirement is emitted once on `@source`.\n */\n private securityHeader(api: Oas): string | null {\n // per-op mode: some operation overrides the global → let each @connect carry its own auth\n if (anyOperationDeclaresSecurity(api)) {\n return null;\n }\n\n // read the document-level (global) security requirement\n const global = globalSecurity(api);\n // no global requirement → keep the headerless @source byte-for-byte\n if (!global || global.length === 0) {\n return null;\n }\n\n // read the scheme definitions (apiKey/http/oauth2/...) the requirement refers to\n const schemes = securitySchemes(api);\n // resolve the global requirement to one header, collecting per-scheme drop warnings\n const { header, warnings } = resolveAuthHeader(global, schemes);\n // surface every dropped scheme loudly (never silently)\n for (const w of warnings) {\n console.warn(w);\n }\n\n if (!header) {\n // flatten the referenced scheme names for the summary message\n const names = global.flatMap((req) => Object.keys(req));\n console.warn(\n `[security] global security requirement (${names.join(', ')}) maps to no header-producing scheme; no auth header emitted on @source.`,\n );\n return null;\n }\n\n // format the resolved header into the @source literal\n return `{ name: \"${header.name}\", value: \"${header.value}\" }`;\n }\n\n private getServerUrl(server: ServerObject | undefined): string {\n if (!server) {\n return 'http://localhost:4010';\n }\n let url: string = server.url;\n if (server.variables) {\n for (const key in server.variables) {\n url = url.replace('{' + key + '}', server.variables[key].default);\n }\n }\n return url;\n }\n}\n"]}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import Oas from 'oas';
|
|
2
|
+
import { SecurityRequirementObject, SecuritySchemeObject, SecuritySchemesObject } from 'oas/types';
|
|
3
|
+
export type AuthHeader = {
|
|
4
|
+
name: string;
|
|
5
|
+
value: string;
|
|
6
|
+
};
|
|
7
|
+
/**
|
|
8
|
+
* Map one OAS security scheme to its connector header, or null when it makes no header.
|
|
9
|
+
* Shared by the @source writer (slice 1) and the per-operation @connect writer (slice 2).
|
|
10
|
+
*/
|
|
11
|
+
export declare function mapSchemeToAuthHeader(scheme: SecuritySchemeObject): AuthHeader | null;
|
|
12
|
+
/** Build a loud warning for a scheme that was not turned into a header. */
|
|
13
|
+
export declare function dropWarning(name: string, scheme: SecuritySchemeObject | undefined): string;
|
|
14
|
+
/**
|
|
15
|
+
* Resolve a security requirement to a single header, reusing the slice-1 rule: flatten the
|
|
16
|
+
* referenced scheme names and pick the first that produces a header; every other scheme is
|
|
17
|
+
* warned about. An absent (`undefined`) or empty (`[]`, i.e. public) requirement makes no
|
|
18
|
+
* header. The caller decides what a null header means in its context (see the mode switch in
|
|
19
|
+
* schemaWriter / operationWriter) and emits the returned warnings with its own prefix.
|
|
20
|
+
*/
|
|
21
|
+
export declare function resolveAuthHeader(securityRequirements: SecurityRequirementObject[] | undefined, securitySchemes: SecuritySchemesObject): {
|
|
22
|
+
header: AuthHeader | null;
|
|
23
|
+
warnings: string[];
|
|
24
|
+
};
|
|
25
|
+
/** Read `components.securitySchemes` (OAS 3) falling back to `securityDefinitions` (Swagger 2.0). */
|
|
26
|
+
export declare function securitySchemes(api: Oas): SecuritySchemesObject;
|
|
27
|
+
/** The spec's global `security` requirement (the document-level default), or undefined. */
|
|
28
|
+
export declare function globalSecurity(api: Oas): SecurityRequirementObject[] | undefined;
|
|
29
|
+
/**
|
|
30
|
+
* True when ANY operation in the spec declares its own `security` (empty `[]` or non-empty).
|
|
31
|
+
* This is the per-source mode switch: when true, a shared `@source` auth header is unsafe
|
|
32
|
+
* (a public op would still leak it, and a different-named override would send both), so auth
|
|
33
|
+
* moves to each operation's `@connect` instead. See the ROADMAP R5 slice 2 notes.
|
|
34
|
+
*/
|
|
35
|
+
export declare function anyOperationDeclaresSecurity(api: Oas): boolean;
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import _ from 'lodash';
|
|
2
|
+
import { OpenAPIV3 } from 'openapi-types';
|
|
3
|
+
// The HTTP methods a path item can hold — reuse the canonical enum rather than a local literal.
|
|
4
|
+
const HTTP_METHODS = Object.values(OpenAPIV3.HttpMethods);
|
|
5
|
+
/**
|
|
6
|
+
* Map one OAS security scheme to its connector header, or null when it makes no header.
|
|
7
|
+
* Shared by the @source writer (slice 1) and the per-operation @connect writer (slice 2).
|
|
8
|
+
*/
|
|
9
|
+
export function mapSchemeToAuthHeader(scheme) {
|
|
10
|
+
switch (scheme.type) {
|
|
11
|
+
case 'apiKey':
|
|
12
|
+
// apiKey carried in a header, e.g. `{ type: apiKey, in: header, name: X-API-Key }`
|
|
13
|
+
if (scheme.in === 'header' && scheme.name) {
|
|
14
|
+
return { name: scheme.name, value: '{$config.apiKey}' };
|
|
15
|
+
}
|
|
16
|
+
return null; // query / cookie are deferred and warned about, not emitted as headers
|
|
17
|
+
case 'http':
|
|
18
|
+
// e.g. `{ type: http, scheme: bearer }`
|
|
19
|
+
if (scheme.scheme === 'bearer') {
|
|
20
|
+
return { name: 'Authorization', value: 'Bearer {$config.token}' };
|
|
21
|
+
}
|
|
22
|
+
// e.g. `{ type: http, scheme: basic }`
|
|
23
|
+
if (scheme.scheme === 'basic') {
|
|
24
|
+
return { name: 'Authorization', value: 'Basic {$config.token}' };
|
|
25
|
+
}
|
|
26
|
+
return null;
|
|
27
|
+
case 'oauth2':
|
|
28
|
+
case 'openIdConnect':
|
|
29
|
+
// both flows ultimately send a bearer token
|
|
30
|
+
return { name: 'Authorization', value: 'Bearer {$config.token}' };
|
|
31
|
+
default:
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
/** Build a loud warning for a scheme that was not turned into a header. */
|
|
36
|
+
export function dropWarning(name, scheme) {
|
|
37
|
+
if (!scheme) {
|
|
38
|
+
return `[security] scheme "${name}" is referenced by global security but not defined; skipped.`;
|
|
39
|
+
}
|
|
40
|
+
if (scheme.type === 'apiKey' && scheme.in === 'query') {
|
|
41
|
+
return `[security] scheme "${name}" is apiKey in query — not emitted as a header (query-param auth deferred).`;
|
|
42
|
+
}
|
|
43
|
+
if (scheme.type === 'apiKey' && scheme.in === 'cookie') {
|
|
44
|
+
return `[security] scheme "${name}" is apiKey in cookie — not emitted as a header (cookie auth deferred).`;
|
|
45
|
+
}
|
|
46
|
+
return `[security] scheme "${name}" (${scheme.type}) not emitted — only the first header-producing scheme of the requirement is mapped.`;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Resolve a security requirement to a single header, reusing the slice-1 rule: flatten the
|
|
50
|
+
* referenced scheme names and pick the first that produces a header; every other scheme is
|
|
51
|
+
* warned about. An absent (`undefined`) or empty (`[]`, i.e. public) requirement makes no
|
|
52
|
+
* header. The caller decides what a null header means in its context (see the mode switch in
|
|
53
|
+
* schemaWriter / operationWriter) and emits the returned warnings with its own prefix.
|
|
54
|
+
*/
|
|
55
|
+
export function resolveAuthHeader(
|
|
56
|
+
// e.g. `[{ bearerAuth: [] }]` (one requirement, one scheme) or `[{ a: [] }, { b: [] }]` (OR of schemes)
|
|
57
|
+
securityRequirements, securitySchemes) {
|
|
58
|
+
// absent or `security: []` (public) → no header, nothing to warn about
|
|
59
|
+
if (!securityRequirements?.length) {
|
|
60
|
+
return { header: null, warnings: [] };
|
|
61
|
+
}
|
|
62
|
+
// flatten to scheme names (across objects = OR, within each = AND), then resolve each to a header
|
|
63
|
+
// (null if it makes none, e.g. apiKey in query)
|
|
64
|
+
const candidates = securityRequirements
|
|
65
|
+
.flatMap((requirement) => Object.keys(requirement))
|
|
66
|
+
.map((name) => {
|
|
67
|
+
const scheme = securitySchemes[name];
|
|
68
|
+
return { name, scheme, header: scheme ? mapSchemeToAuthHeader(scheme) : null };
|
|
69
|
+
});
|
|
70
|
+
// the first scheme that produces a header wins; every other referenced scheme is warned about
|
|
71
|
+
const chosen = candidates.find((candidate) => candidate.header);
|
|
72
|
+
const warnings = candidates
|
|
73
|
+
.filter((candidate) => candidate !== chosen)
|
|
74
|
+
.map((candidate) => dropWarning(candidate.name, candidate.scheme));
|
|
75
|
+
return { header: chosen?.header ?? null, warnings };
|
|
76
|
+
}
|
|
77
|
+
/** Read `components.securitySchemes` (OAS 3) falling back to `securityDefinitions` (Swagger 2.0). */
|
|
78
|
+
export function securitySchemes(api) {
|
|
79
|
+
const def = api.getDefinition();
|
|
80
|
+
// oas-normalize usually up-converts Swagger 2.0 → components.securitySchemes; the
|
|
81
|
+
// securityDefinitions fallback (same shape, exposed via OASDocument's index signature) is a
|
|
82
|
+
// safety net. refs are already dereferenced by oas, so the shape is a plain SecuritySchemesObject.
|
|
83
|
+
return (def.components?.securitySchemes ?? def.securityDefinitions ?? {});
|
|
84
|
+
}
|
|
85
|
+
/** The spec's global `security` requirement (the document-level default), or undefined. */
|
|
86
|
+
export function globalSecurity(api) {
|
|
87
|
+
return api.getDefinition().security;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* True when ANY operation in the spec declares its own `security` (empty `[]` or non-empty).
|
|
91
|
+
* This is the per-source mode switch: when true, a shared `@source` auth header is unsafe
|
|
92
|
+
* (a public op would still leak it, and a different-named override would send both), so auth
|
|
93
|
+
* moves to each operation's `@connect` instead. See the ROADMAP R5 slice 2 notes.
|
|
94
|
+
*/
|
|
95
|
+
export function anyOperationDeclaresSecurity(api) {
|
|
96
|
+
const paths = api.getDefinition().paths ?? {};
|
|
97
|
+
for (const pathName of Object.keys(paths)) {
|
|
98
|
+
const path = paths[pathName];
|
|
99
|
+
if (!path)
|
|
100
|
+
continue;
|
|
101
|
+
for (const method of HTTP_METHODS) {
|
|
102
|
+
const op = path[method];
|
|
103
|
+
// own property (not inherited) — an explicit `security: []` is still a declaration
|
|
104
|
+
if (op && _.has(op, 'security')) {
|
|
105
|
+
return true;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return false;
|
|
110
|
+
}
|
|
111
|
+
//# sourceMappingURL=security.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"security.js","sourceRoot":"","sources":["../../../src/oas/io/security.ts"],"names":[],"mappings":"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","sourcesContent":["import _ from 'lodash';\nimport Oas from 'oas';\nimport { SecurityRequirementObject, SecuritySchemeObject, SecuritySchemesObject } from 'oas/types';\nimport { OpenAPIV3 } from 'openapi-types';\n\n// One auth header: the header name and its value template (e.g. Authorization / \"Bearer {$config.token}\").\nexport type AuthHeader = { name: string; value: string };\n\n// The HTTP methods a path item can hold — reuse the canonical enum rather than a local literal.\nconst HTTP_METHODS = Object.values(OpenAPIV3.HttpMethods);\n\n/**\n * Map one OAS security scheme to its connector header, or null when it makes no header.\n * Shared by the @source writer (slice 1) and the per-operation @connect writer (slice 2).\n */\nexport function mapSchemeToAuthHeader(scheme: SecuritySchemeObject): AuthHeader | null {\n switch (scheme.type) {\n case 'apiKey':\n // apiKey carried in a header, e.g. `{ type: apiKey, in: header, name: X-API-Key }`\n if (scheme.in === 'header' && scheme.name) {\n return { name: scheme.name, value: '{$config.apiKey}' };\n }\n return null; // query / cookie are deferred and warned about, not emitted as headers\n case 'http':\n // e.g. `{ type: http, scheme: bearer }`\n if (scheme.scheme === 'bearer') {\n return { name: 'Authorization', value: 'Bearer {$config.token}' };\n }\n // e.g. `{ type: http, scheme: basic }`\n if (scheme.scheme === 'basic') {\n return { name: 'Authorization', value: 'Basic {$config.token}' };\n }\n return null;\n case 'oauth2':\n case 'openIdConnect':\n // both flows ultimately send a bearer token\n return { name: 'Authorization', value: 'Bearer {$config.token}' };\n default:\n return null;\n }\n}\n\n/** Build a loud warning for a scheme that was not turned into a header. */\nexport function dropWarning(name: string, scheme: SecuritySchemeObject | undefined): string {\n if (!scheme) {\n return `[security] scheme \"${name}\" is referenced by global security but not defined; skipped.`;\n }\n if (scheme.type === 'apiKey' && scheme.in === 'query') {\n return `[security] scheme \"${name}\" is apiKey in query — not emitted as a header (query-param auth deferred).`;\n }\n if (scheme.type === 'apiKey' && scheme.in === 'cookie') {\n return `[security] scheme \"${name}\" is apiKey in cookie — not emitted as a header (cookie auth deferred).`;\n }\n return `[security] scheme \"${name}\" (${scheme.type}) not emitted — only the first header-producing scheme of the requirement is mapped.`;\n}\n\n/**\n * Resolve a security requirement to a single header, reusing the slice-1 rule: flatten the\n * referenced scheme names and pick the first that produces a header; every other scheme is\n * warned about. An absent (`undefined`) or empty (`[]`, i.e. public) requirement makes no\n * header. The caller decides what a null header means in its context (see the mode switch in\n * schemaWriter / operationWriter) and emits the returned warnings with its own prefix.\n */\nexport function resolveAuthHeader(\n // e.g. `[{ bearerAuth: [] }]` (one requirement, one scheme) or `[{ a: [] }, { b: [] }]` (OR of schemes)\n securityRequirements: SecurityRequirementObject[] | undefined,\n securitySchemes: SecuritySchemesObject,\n): { header: AuthHeader | null; warnings: string[] } {\n // absent or `security: []` (public) → no header, nothing to warn about\n if (!securityRequirements?.length) {\n return { header: null, warnings: [] };\n }\n\n // flatten to scheme names (across objects = OR, within each = AND), then resolve each to a header\n // (null if it makes none, e.g. apiKey in query)\n const candidates = securityRequirements\n .flatMap((requirement) => Object.keys(requirement))\n .map((name) => {\n const scheme = securitySchemes[name];\n return { name, scheme, header: scheme ? mapSchemeToAuthHeader(scheme) : null };\n });\n\n // the first scheme that produces a header wins; every other referenced scheme is warned about\n const chosen = candidates.find((candidate) => candidate.header);\n const warnings = candidates\n .filter((candidate) => candidate !== chosen)\n .map((candidate) => dropWarning(candidate.name, candidate.scheme));\n\n return { header: chosen?.header ?? null, warnings };\n}\n\n/** Read `components.securitySchemes` (OAS 3) falling back to `securityDefinitions` (Swagger 2.0). */\nexport function securitySchemes(api: Oas): SecuritySchemesObject {\n const def = api.getDefinition();\n // oas-normalize usually up-converts Swagger 2.0 → components.securitySchemes; the\n // securityDefinitions fallback (same shape, exposed via OASDocument's index signature) is a\n // safety net. refs are already dereferenced by oas, so the shape is a plain SecuritySchemesObject.\n return (def.components?.securitySchemes ?? def.securityDefinitions ?? {}) as SecuritySchemesObject;\n}\n\n/** The spec's global `security` requirement (the document-level default), or undefined. */\nexport function globalSecurity(api: Oas): SecurityRequirementObject[] | undefined {\n return api.getDefinition().security;\n}\n\n/**\n * True when ANY operation in the spec declares its own `security` (empty `[]` or non-empty).\n * This is the per-source mode switch: when true, a shared `@source` auth header is unsafe\n * (a public op would still leak it, and a different-named override would send both), so auth\n * moves to each operation's `@connect` instead. See the ROADMAP R5 slice 2 notes.\n */\nexport function anyOperationDeclaresSecurity(api: Oas): boolean {\n const paths = api.getDefinition().paths ?? {};\n\n for (const pathName of Object.keys(paths)) {\n const path = paths[pathName];\n if (!path) continue;\n\n for (const method of HTTP_METHODS) {\n const op = path[method];\n // own property (not inherited) — an explicit `security: []` is still a declaration\n if (op && _.has(op, 'security')) {\n return true;\n }\n }\n }\n return false;\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"root":["../src/index.ts","../src/versions.ts","../src/cli/json.ts","../src/cli/oas.ts","../src/cli/oas-helpers/index.ts","../src/json/index.ts","../src/json/io/index.ts","../src/json/io/writer.ts","../src/json/walker/index.ts","../src/json/walker/jsoncontext.ts","../src/json/walker/jsongen.ts","../src/json/walker/naming.ts","../src/json/walker/log/trace.ts","../src/json/walker/types/index.ts","../src/json/walker/types/jsonarray.ts","../src/json/walker/types/jsonobj.ts","../src/json/walker/types/jsonscalar.ts","../src/json/walker/types/jsontype.ts","../src/oas/index.ts","../src/oas/oascontext.ts","../src/oas/oasgen.ts","../src/oas/generator/typescollector.ts","../src/oas/io/errorswriter.ts","../src/oas/io/operationwriter.ts","../src/oas/io/schemawriter.ts","../src/oas/io/writer.ts","../src/oas/log/trace.ts","../src/oas/mapper/index.ts","../src/oas/mapper/loader.ts","../src/oas/mapper/namemapper.ts","../src/oas/mapper/types.ts","../src/oas/nodes/arr.ts","../src/oas/nodes/body.ts","../src/oas/nodes/circularref.ts","../src/oas/nodes/comp.ts","../src/oas/nodes/delete.ts","../src/oas/nodes/en.ts","../src/oas/nodes/entity.ts","../src/oas/nodes/factory.ts","../src/oas/nodes/get.ts","../src/oas/nodes/itype.ts","../src/oas/nodes/interfacepromotion.ts","../src/oas/nodes/internal.ts","../src/oas/nodes/map.ts","../src/oas/nodes/obj.ts","../src/oas/nodes/op.ts","../src/oas/nodes/param.ts","../src/oas/nodes/patch.ts","../src/oas/nodes/post.ts","../src/oas/nodes/prop.ts","../src/oas/nodes/proparray.ts","../src/oas/nodes/propcircref.ts","../src/oas/nodes/propcomp.ts","../src/oas/nodes/propen.ts","../src/oas/nodes/propmap.ts","../src/oas/nodes/propobj.ts","../src/oas/nodes/propref.ts","../src/oas/nodes/propscalar.ts","../src/oas/nodes/put.ts","../src/oas/nodes/ref.ts","../src/oas/nodes/refcircref.ts","../src/oas/nodes/referenceobject.ts","../src/oas/nodes/res.ts","../src/oas/nodes/scalar.ts","../src/oas/nodes/type.ts","../src/oas/nodes/typeutils.ts","../src/oas/nodes/union.ts","../src/oas/prompts/prompt.ts","../src/oas/prompts/theme.ts","../src/oas/prompts/base/theme.ts","../src/oas/prompts/base/utils.ts","../src/oas/schemas/index.ts","../src/oas/utils/gql.ts","../src/oas/utils/naming.ts","../src/oas/visitor/generatorvisitor.ts","../src/oas/visitor/nodevisitor.ts","../src/oas/visitor/example.ts","../src/oas/visitor/index.ts","../src/tests/runners.ts"],"version":"5.8.2"}
|
|
1
|
+
{"root":["../src/index.ts","../src/versions.ts","../src/cli/json.ts","../src/cli/oas.ts","../src/cli/oas-helpers/index.ts","../src/json/index.ts","../src/json/io/index.ts","../src/json/io/writer.ts","../src/json/walker/index.ts","../src/json/walker/jsoncontext.ts","../src/json/walker/jsongen.ts","../src/json/walker/naming.ts","../src/json/walker/log/trace.ts","../src/json/walker/types/index.ts","../src/json/walker/types/jsonarray.ts","../src/json/walker/types/jsonobj.ts","../src/json/walker/types/jsonscalar.ts","../src/json/walker/types/jsontype.ts","../src/oas/index.ts","../src/oas/oascontext.ts","../src/oas/oasgen.ts","../src/oas/generator/typescollector.ts","../src/oas/io/errorswriter.ts","../src/oas/io/operationwriter.ts","../src/oas/io/schemawriter.ts","../src/oas/io/security.ts","../src/oas/io/writer.ts","../src/oas/log/trace.ts","../src/oas/mapper/index.ts","../src/oas/mapper/loader.ts","../src/oas/mapper/namemapper.ts","../src/oas/mapper/types.ts","../src/oas/nodes/arr.ts","../src/oas/nodes/body.ts","../src/oas/nodes/circularref.ts","../src/oas/nodes/comp.ts","../src/oas/nodes/delete.ts","../src/oas/nodes/en.ts","../src/oas/nodes/entity.ts","../src/oas/nodes/factory.ts","../src/oas/nodes/get.ts","../src/oas/nodes/itype.ts","../src/oas/nodes/interfacepromotion.ts","../src/oas/nodes/internal.ts","../src/oas/nodes/map.ts","../src/oas/nodes/obj.ts","../src/oas/nodes/op.ts","../src/oas/nodes/param.ts","../src/oas/nodes/patch.ts","../src/oas/nodes/post.ts","../src/oas/nodes/prop.ts","../src/oas/nodes/proparray.ts","../src/oas/nodes/propcircref.ts","../src/oas/nodes/propcomp.ts","../src/oas/nodes/propen.ts","../src/oas/nodes/propmap.ts","../src/oas/nodes/propobj.ts","../src/oas/nodes/propref.ts","../src/oas/nodes/propscalar.ts","../src/oas/nodes/put.ts","../src/oas/nodes/ref.ts","../src/oas/nodes/refcircref.ts","../src/oas/nodes/referenceobject.ts","../src/oas/nodes/res.ts","../src/oas/nodes/scalar.ts","../src/oas/nodes/type.ts","../src/oas/nodes/typeutils.ts","../src/oas/nodes/union.ts","../src/oas/prompts/prompt.ts","../src/oas/prompts/theme.ts","../src/oas/prompts/base/theme.ts","../src/oas/prompts/base/utils.ts","../src/oas/schemas/index.ts","../src/oas/utils/gql.ts","../src/oas/utils/naming.ts","../src/oas/visitor/generatorvisitor.ts","../src/oas/visitor/nodevisitor.ts","../src/oas/visitor/example.ts","../src/oas/visitor/index.ts","../src/tests/runners.ts"],"version":"5.8.2"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "apollo-conn-gen",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.0",
|
|
4
4
|
"description": "Library to generate Apollo connector schemas from OpenAPI specs or JSON payloads. Also includes a CLI to generate connectors from the command line.",
|
|
5
5
|
"author": "Fernando @ Apollo",
|
|
6
6
|
"license": "MIT",
|