orbit-code-ai 0.1.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.
@@ -0,0 +1,645 @@
1
+ ---
2
+ name: apic-api
3
+ description: Generate a complete IBM API Connect (APIC) API definition for the DataPower API Gateway — full OpenAPI 2.0 (or 3.0) YAML including the x-ibm-configuration assembly (middleware logging, JWT validate, token-expiration switch, ESB response envelope, and catch handlers). Standard NHC/Qiwa middleware is baked in with opt-out flags.
4
+ trigger: /apic-api
5
+ ---
6
+
7
+ # /apic-api
8
+
9
+ Generate a full APIC API definition (the `*.yaml` you import into API Connect / publish to the
10
+ DataPower API Gateway). Output mirrors the house template used by the live AhelApp / Muamalaty /
11
+ AmerApp APIs: a Swagger/OpenAPI contract **plus** the complete `x-ibm-configuration` assembly.
12
+
13
+ ## Usage
14
+
15
+ ```
16
+ /apic-api <ApiName> <GET|POST> <basePath> [description: params, backend URL, request/response fields]
17
+
18
+ Flags:
19
+ --openapi 2.0|3.0 target spec version — USER'S CHOICE. If omitted, ask the user which one
20
+ they want before generating. Orbit does not assume a version.
21
+ --crypto <name> jws-crypto object for JWT validation (default: <appsegment>-crypto-object)
22
+ --backend-auth <name> APIC variable injected as backend auth header (e.g. servicenow-auth)
23
+ --no-jwt omit jwt-validate + Check Expiration + 400 throw (no token enforcement)
24
+ --no-logging omit the middleware UpsertMDLogQ logging invoke (AmerApp-style)
25
+ --no-envelope return the raw backend body instead of the {Header,Body} ESB envelope
26
+ --no-cors disable CORS
27
+
28
+ Backend-call tuning (all optional — infer sensible values from the prompt if not given):
29
+ --backend-verb V backend invoke verb: GET|POST|keep (default = API verb; "keep" forwards the
30
+ caller's verb, AmerApp-style)
31
+ --backend-type T backend response handling: json|detect (default json; "detect" =
32
+ content-type based, used by AmerApp)
33
+ --invoke-version V backend invoke policy version: 2.2.0|2.3.0 (default 2.2.0; 2.3.0 adds
34
+ stop-on-error + graphql-send-type)
35
+ --backend-auth-header H header the --backend-auth value is set on (default Authorization;
36
+ Muamalaty uses "Token")
37
+ --backend-headers k=v,... extra STATIC headers set before the backend call
38
+ (e.g. REST-Framework-Version=4)
39
+ --pass-headers a,b,... headers FORWARDED to the backend → invoke header-control allowlist
40
+ (e.g. Token,rolename). Without this, header-control is blocklist [].
41
+ --query a,b,... backend query params → invoke parameter-control allowlist AND paths query
42
+ params. CRITICAL: every query param the backend needs must be listed, or the
43
+ gateway strips it.
44
+ --parse P backend parse: json (inline document_type json, default) | default
45
+ (apic-default-parsesettings, AmerApp-style)
46
+ --use-content-type add use-content-type: true to the backend parse (Muamalaty/AmerApp)
47
+ --envelope-placement P where Prepare Response Header goes: in-case (default, Ahel/AmerApp) |
48
+ top-level (after the switch, Muamalaty)
49
+
50
+ Examples:
51
+ /apic-api AmerAppGetUserInfo GET /v1/AmerApp \
52
+ backend $(erp-base-url)/hcmRestApi/resources/latest/workers?onlyData=true; query param expand
53
+ /apic-api AhelUpdateCustomer POST /v1/AhelApp \
54
+ backend $(servicenow-url)api/.../UpdateCustomerPersonalInformation; --backend-auth servicenow-auth; \
55
+ body fields: customerSysId,email,idNumber,name,avatar,gender,dateOfBirth,attachment_records
56
+ ```
57
+
58
+ ## Variables to Derive
59
+
60
+ From the inputs:
61
+ - `{ApiTitle}` = Title Case of ApiName, e.g. `AmerApp Get User Info`
62
+ - `{x-ibm-name}` = kebab-case of ApiName, e.g. `amerapp-get-userinfo` (also the file stem)
63
+ - `{AppSegment}` = the app portion of basePath after `/v1/`, e.g. `AmerApp`
64
+ - `{appsegmentlower}` = lowercase AppSegment, e.g. `amerapp`
65
+ - `{version}` = `1.0.0` unless supplied
66
+ - `{operation-path}` = the API's own path, **derived from the API name** (PascalCase, leading slash):
67
+ `Get Customer Balance` → `/GetCustomerBalance`, like `AmerApp Get User Info` → `/GetUserInfo`.
68
+ **NEVER** take it from the backend target-url's path (the backend path is unrelated to the
69
+ exposed API path). Use a supplied path only if the user gives one explicitly.
70
+ - `{verb}` = GET or POST (lowercase in `paths`)
71
+ - `{crypto-object}` = `--crypto` or `{appsegmentlower}-crypto-object`
72
+ - `{backend-target-url}` = backend URL from the description (keep `$(...)` APIC vars verbatim,
73
+ including claim injections like `?userEmail=$(decoded.claims.EmailAddress)`)
74
+ - `{backend-auth-var}` = `--backend-auth` value (only emitted if provided)
75
+ - `{backend-auth-header}` = `--backend-auth-header` or `Authorization` (Muamalaty: `Token`)
76
+ - `{backend-verb}` = `--backend-verb` or the API verb (may be `keep` — forwards caller's verb)
77
+ - `{backend-type}` = `--backend-type` or `json` (or `detect`)
78
+ - `{query}` = list of backend query params (from `--query` or inferred from the backend URL / prompt)
79
+ - `{pass-headers}` = `--pass-headers` (headers forwarded to backend, e.g. `Token,rolename`)
80
+
81
+ Output filename: `{x-ibm-name}_{version}.yaml`
82
+
83
+ ---
84
+
85
+ ## Clarify before generating (do this FIRST)
86
+
87
+ Some values are **unguessable and deploy-critical** — never fabricate them from the API name.
88
+ Before generating, collect any of the following that the prompt did not supply, and **ask the user
89
+ in a single batched follow-up** (alongside the 2.0/3.0 question). Do NOT invent plausible-looking
90
+ values for these:
91
+
92
+ - **OpenAPI version** — 2.0 or 3.0 (always ask if unspecified).
93
+ - **Channel / app + basePath** — e.g. `/v1/AmerApp`. The app segment also drives naming. There is
94
+ no way to infer this from the operation name; ask (offer the known channels if helpful).
95
+ - **JWT crypto object** — e.g. `$(amerapp-crypto-object)`. Tied to the channel, not the operation.
96
+ Only needed when JWT is enabled (default). Ask; do not derive it from the API name.
97
+ - **Backend auth** — the APIC variable and the header it goes on (`Authorization` vs `Token`), if
98
+ the backend needs auth. Ask only if the prompt implies the backend is protected.
99
+
100
+ If the user prefers not to answer, fall back to clearly-marked placeholders
101
+ (`$(CHANGEME-crypto-object)`, `basePath: /v1/CHANGEME`) and list every placeholder in the summary —
102
+ never a guess that looks real.
103
+
104
+ ## Generation rules
105
+
106
+ 1. **The spec version is the user's choice — Orbit supports both 2.0 and 3.0 equally.** Never
107
+ silently assume a version. If `--openapi` was not passed and the user did not state a version,
108
+ **ask** ("OpenAPI 2.0 or 3.0?") before generating. Then emit 2.0 from the skeleton below, or
109
+ apply the 3.0 transform section.
110
+ 2. **The `x-ibm-configuration.assembly` block is identical in 2.0 and 3.0.** Only the *contract*
111
+ (paths/definitions/security/schemes) changes between versions.
112
+ 3. **Paste the frozen gatewayscript verbatim.** The JS strings below must be byte-exact — DataPower
113
+ rejects malformed assembly. Do not "clean up" or reformat them.
114
+ 4. **GET vs POST**: GET → query/header params, no request `map`, no `definitions`. POST with body
115
+ fields → add a request `map` policy + a `Request` definition (AhelApp pattern).
116
+ 5. Respect opt-out flags by omitting the corresponding policy (see annotations in the skeleton).
117
+ 6. **Preserve every `$(...)` APIC variable reference exactly** — never strip the wrapper. In
118
+ particular `jws-crypto` MUST be `$({crypto-object})` (e.g. `$(amerapp-crypto-object)`), not the
119
+ bare name. A bare value is treated by the gateway as a literal string, not a variable, and breaks
120
+ JWT validation. Same for `target-url`, backend auth values, and any `$(...)` in the fragments.
121
+ 7. **Backend `parameter-control` allowlist MUST list every backend query param.** An empty allowlist
122
+ silently strips query params at the gateway. The same param set appears as `in: query` entries in
123
+ `paths`. Likewise, any header forwarded to the backend (`--pass-headers`) goes in BOTH the invoke
124
+ `header-control` allowlist AND as an `in: header` path param.
125
+ 8. **Match policy variants to the backend, using the Policy variations reference.** Pick backend
126
+ `verb`/`backend-type`/`invoke-version`, the auth header name, parse form, and envelope placement
127
+ from the prompt; when unspecified, default to the skeleton (GET/POST, json, 2.2.0, Authorization,
128
+ inline json parse, in-case envelope). When in doubt about which shape a known backend uses, ask.
129
+ 9. **Logging must be consistent.** Unless `--no-logging`, emit BOTH the `Log-set-variable` and the
130
+ `Log-invoke` (UpsertMDLogQ) policies between Prepare Request and jwt-validate, and report
131
+ "MW logging on" in the summary. If you omit the block, report "off". The summary must always match
132
+ the assembly that was actually written.
133
+ 10. **Do not hand-append allowlisted query params to the backend `target-url`.** Params listed in the
134
+ invoke `parameter-control` allowlist are forwarded automatically. Only manually append values
135
+ that are NOT request params — i.e. injected claims like `?userEmail=$(decoded.claims.EmailAddress)`
136
+ (Muamalaty) or `&q=...$(decoded.claims.EmailAddress)` (AmerApp). Appending
137
+ `?accountId=$(request.parameters.accountId)` for an already-allowlisted `accountId` is wrong.
138
+ 11. **Reproduce every gatewayscript `source` character-for-character.** Do not strip comments, blank
139
+ lines, or re-indent. The single-line quoted form (`"...\n..."`) is canonical and must be copied
140
+ verbatim; if a block scalar (`|-` / `|2-`) is used, preserve its body exactly.
141
+
142
+ ---
143
+
144
+ ## OpenAPI 2.0 skeleton
145
+
146
+ Assemble the file in this order. Fill `{slots}`; paste frozen fragments unchanged.
147
+
148
+ ```yaml
149
+ swagger: '2.0'
150
+ info:
151
+ title: {ApiTitle}
152
+ x-ibm-name: {x-ibm-name}
153
+ version: {version}
154
+ x-ibm-configuration:
155
+ cors:
156
+ enabled: true # --no-cors → false
157
+ gateway: datapower-api-gateway
158
+ type: rest
159
+ phase: realized
160
+ enforced: true
161
+ testable: true
162
+ assembly:
163
+ execute:
164
+ # ── POST-WITH-BODY ONLY: leading parse of the incoming request (AhelApp). Omit for GET. ──
165
+ # Place this as the FIRST policy, before logging, so message.body is parsed for the map.
166
+ - parse:
167
+ version: 2.2.0
168
+ title: parse
169
+ parse-settings-reference:
170
+ parse-settings:
171
+ document_type: json
172
+ max_value_length: 0
173
+ max_width: 0
174
+
175
+ # ── FROZEN: Prepare Request (RefId / CallerReqTime guard + custom headers) ──
176
+ - gatewayscript:
177
+ version: 2.0.0
178
+ title: Prepare Request
179
+ source: "var reqtime;\nvar RefId = \"\";\n\ntry {\n\tRefId = context.get('message.headers.RefId');\n}\ncatch(err) {\n\tRefId = null;\n}\n\nif (context.get('message.headers.CallerReqTime') == null) {\n\treqtime = new Date();\n}\nelse {\n\treqtime = context.get('message.headers.CallerReqTime');\n}\n\nif (RefId == null) {\n\tcontext.message.status.code = 400;\n\tcontext.message.status.reason = 'RefId is missing in the header';\n\tvar error = new Error('RefId is missing in the header');\n\terror.code = \"400\";\n\tcontext.reject('RefId is missing in the header', error);\n}\nelse {\n\tRefId = context.get('message.headers.RefId');\n}\n\nvar Chid = context.get('client.org.name');\nvar uuid1 = context.get('message.headers.X-Global-Transaction-ID');\n\ncontext.set('context.customheaders.CallerReqTime',reqtime);\ncontext.set('context.customheaders.RefId',RefId);\ncontext.set('context.customheaders.ChId',Chid);\ncontext.set('context.customheaders.ReqID',uuid1);"
180
+
181
+ # ── FROZEN: middleware logging — OMIT this set-variable + invoke if --no-logging ──
182
+ - set-variable:
183
+ version: 2.0.0
184
+ title: Log-set-variable
185
+ actions:
186
+ - set: message.headers.Authorization
187
+ value: $(nhc-mw-auth)
188
+ type: string
189
+ - invoke:
190
+ version: 2.0.0
191
+ title: Log-invoke
192
+ backend-type: json
193
+ header-control:
194
+ type: blocklist
195
+ values: []
196
+ parameter-control:
197
+ type: allowlist
198
+ values: []
199
+ timeout: 60
200
+ verb: POST
201
+ chunked-uploads: true
202
+ persistent-connection: true
203
+ follow-redirects: false
204
+ keep-payload: true
205
+ output: outResp
206
+ target-url: >-
207
+ $(sakani-mw-base-url)/MW/MWLogServices/GeneralLog/UpsertMDLogQ?Environment=$(env.path)&ServiceName=$(api.name)&UserName=$(client.app.id)&RequestDt=$(system.datetime)
208
+
209
+ # ── FROZEN: JWT — OMIT jwt-validate + Check Expiration if --no-jwt (then switch goes always-200) ──
210
+ - jwt-validate:
211
+ version: 2.0.0
212
+ title: jwt-validate
213
+ jwt: request.headers.authorization
214
+ output-claims: decoded.claims
215
+ jws-jwk: ''
216
+ jws-crypto: $({crypto-object})
217
+ - gatewayscript:
218
+ version: 2.0.0
219
+ title: Check Expiration
220
+ source: "\nvar tokenDate = context.get('decoded.claims.ExpirationDate');\nvar expirationDate = new Date(tokenDate);\n\n// Get the current time\nvar currentTime = new Date();\n\n// Check if the token has expired\nif (expirationDate <= currentTime) {\n context.set('message.status.code',400);\n} else {\n context.set('message.status.code',200);\n}"
221
+
222
+ - switch:
223
+ version: 2.0.0
224
+ title: switch
225
+ case:
226
+ - condition: ($statusCode() = 200)
227
+ execute:
228
+ # Backend auth + static headers — emit only if --backend-auth / --backend-headers given.
229
+ # Header name = {backend-auth-header} (default Authorization; Muamalaty uses "Token").
230
+ # Add one action per --backend-headers entry (e.g. REST-Framework-Version: '4', AmerApp).
231
+ - set-variable:
232
+ version: 2.0.0
233
+ title: set-variable
234
+ actions:
235
+ - set: message.headers.{backend-auth-header}
236
+ value: $({backend-auth-var})
237
+ type: string
238
+ # - set: message.headers.REST-Framework-Version # example extra static header
239
+ # value: '4'
240
+ # type: string
241
+
242
+ # POST-with-body ONLY → request map (see "POST request map" section). Omit for GET.
243
+
244
+ # ── Backend call ──
245
+ # verb: {backend-verb} (GET|POST|keep — "keep" forwards caller's verb, AmerApp)
246
+ # backend-type: {backend-type} (json default | detect, AmerApp)
247
+ # version 2.3.0 ⇒ also include stop-on-error: [] and graphql-send-type: detect (see variations ref)
248
+ - invoke:
249
+ version: 2.2.0 # or 2.3.0
250
+ title: invoke
251
+ backend-type: json # or detect
252
+ header-control:
253
+ type: blocklist # → allowlist with {pass-headers} when forwarding custom headers (Muamalaty: Token, rolename)
254
+ values: []
255
+ parameter-control:
256
+ type: allowlist
257
+ values: [] # MUST list every backend query param ({query}); empty = params stripped
258
+ timeout: 60
259
+ verb: {verb} # or "keep"
260
+ http-version: HTTP/1.1
261
+ chunked-uploads: true
262
+ persistent-connection: true
263
+ cache-response: protocol
264
+ cache-ttl: 900
265
+ websocket-upgrade: false
266
+ # 2.3.0 only: stop-on-error: [] and graphql-send-type: detect
267
+ target-url: >-
268
+ {backend-target-url}
269
+ # Backend parse — pick ONE form (see --parse / variations ref):
270
+ # inline json (default) ── or ── named default: apic-default-parsesettings (AmerApp)
271
+ # add `use-content-type: true` when --use-content-type (Muamalaty/AmerApp).
272
+ - parse:
273
+ version: 2.2.0
274
+ title: parse
275
+ parse-settings-reference:
276
+ parse-settings:
277
+ document_type: json
278
+
279
+ # ── FROZEN: Prepare Response Header — OMIT if --no-envelope. ──
280
+ # Default placement: HERE (inside the 200 case, Ahel/AmerApp).
281
+ # If --envelope-placement top-level (Muamalaty): move this policy OUT of the switch,
282
+ # to top-level execute AFTER the switch and BEFORE the log policy.
283
+ - gatewayscript:
284
+ version: 2.0.0
285
+ title: Prepare Response Header
286
+ source: "var code = context.get('message.status.code');\nvar reason = context.get('message.status.reason');\nvar output = context.get('message.body');\n\nvar response = {\n\t\"Header\": {\n\t\t\"ResTime\": new Date(),\n\t\t\"ChId\": context.get('context.customheaders.ChId'),\n\t\t\"RefId\": context.get('context.customheaders.RefId'),\n\t\t\"ReqID\": context.get('context.customheaders.ReqID'),\n\t\t\"Status\": {\n \"Code\": code,\n\t\t\t\"Description\": reason\n\t\t}\n\t},\n\t\"Body\": output\n};\n\nsession.output.write(response);"
287
+ - condition: ($statusCode() = 400)
288
+ execute:
289
+ - throw:
290
+ version: 2.1.0
291
+ title: throw
292
+ name: ExpiredToken
293
+ error-status-code: '403'
294
+ error-status-reason: forbidden
295
+ message: JWT Token Expired
296
+
297
+ - log:
298
+ version: 2.1.0
299
+ title: log
300
+ log-level: default
301
+ mode: gather-and-send
302
+ finally: []
303
+ # ── FROZEN: catch (ExpiredToken + default), both write the ESB error envelope ──
304
+ catch:
305
+ - errors:
306
+ - ExpiredToken
307
+ execute:
308
+ - gatewayscript:
309
+ version: 2.0.0
310
+ title: ExpiredToken
311
+ source: |2-
312
+
313
+ var code = context.get('error.status.code');
314
+ var reason = context.get('error.message');
315
+ var error = { "Header":{
316
+ "ResTime": new Date(),
317
+ "ChId": context.get('context.customheaders.ChId'),
318
+ "RefId": context.get('context.customheaders.RefId'),
319
+ "ReqID": context.get('context.customheaders.ReqID'),
320
+ "Status": {
321
+ "Code": code,
322
+ "Description": reason
323
+ }
324
+ }
325
+ };
326
+ context.message.statusCode = code;
327
+ session.output.write(error);
328
+ - default:
329
+ - gatewayscript:
330
+ version: 2.0.0
331
+ title: DeFaultCatch
332
+ source: |2-
333
+
334
+ var code = context.get('error.status.code');
335
+ var reason = context.get('error.message');
336
+ var error = { "Header":{
337
+ "ResTime": new Date(),
338
+ "ChId": context.get('context.customheaders.ChId'),
339
+ "RefId": context.get('context.customheaders.RefId'),
340
+ "ReqID": context.get('context.customheaders.ReqID'),
341
+ "Status": {
342
+ "Code": code,
343
+ "Description": reason
344
+ }
345
+ }
346
+ };
347
+ context.message.statusCode = code;
348
+ session.output.write(error);
349
+ properties:
350
+ target-url:
351
+ value: http://example.com/operation-name
352
+ description: The URL of the target service
353
+ encoded: false
354
+ activity-log:
355
+ enabled: true
356
+ success-content: activity
357
+ error-content: payload
358
+ paths:
359
+ {operation-path}:
360
+ parameters: # path-level shared params (RefId may be required: true, Muamalaty)
361
+ - name: RefId
362
+ in: header
363
+ type: string
364
+ # required: true # add when RefId is mandatory (Muamalaty)
365
+ - name: CallerReqTime
366
+ in: header
367
+ type: string
368
+ {verb}: # get: or post:
369
+ responses:
370
+ '200':
371
+ description: success
372
+ schema:
373
+ type: string
374
+ parameters:
375
+ # Emit, in this order:
376
+ # 1. one entry per backend query param ({query}); add `required: true` where mandatory
377
+ # GET example: - { name: inboxId, in: query, type: string, required: true }
378
+ # 2. POST-with-body: - { name: body, in: body, required: true, schema: { $ref: '#/definitions/Request' } }
379
+ # 3. authorization header (below)
380
+ # 4. any extra forwarded headers, e.g. rolename (Muamalaty, required: true)
381
+ - name: authorization
382
+ required: true
383
+ in: header
384
+ type: string
385
+ # - name: rolename # example: custom forwarded header (Muamalaty)
386
+ # required: true
387
+ # in: header
388
+ # type: string
389
+ securityDefinitions:
390
+ clientID:
391
+ type: apiKey
392
+ in: header
393
+ name: X-IBM-Client-Id
394
+ clientSecret:
395
+ type: apiKey
396
+ name: X-IBM-Client-Secret
397
+ in: header
398
+ security:
399
+ - clientID: []
400
+ clientSecret: []
401
+ schemes:
402
+ - https
403
+ basePath: {basePath}
404
+ # definitions: only for POST-with-body (see below)
405
+ ```
406
+
407
+ ---
408
+
409
+ ## POST request map (only when body fields are given)
410
+
411
+ Insert this `map` **inside the 200 case, before the backend `invoke`**, and add the matching
412
+ `definitions` block. Pattern from AhelApp — first output field defaults from a JWT claim, the rest
413
+ copy through from input:
414
+
415
+ ```yaml
416
+ - map:
417
+ inputs:
418
+ input:
419
+ schema:
420
+ $ref: '#/definitions/Request'
421
+ variable: message.body
422
+ outputs:
423
+ output:
424
+ schema:
425
+ $ref: '#/definitions/Request'
426
+ variable: message.body
427
+ actions:
428
+ - set: output.customerSysId
429
+ default: $(decoded.claims.SysId) # claim-defaulted field (optional)
430
+ - set: output.<field>
431
+ from: input.<field> # one per remaining body field
432
+ version: 2.1.0
433
+ title: map
434
+ options:
435
+ mapEmulateV4EmptyJSONObject: false
436
+ mapEmulateV4DefaultRequiredProps: false
437
+ mapEnablePostProcessingJSON: false
438
+ mapReferenceLimit: 1
439
+ mapResolveApicVariables: true
440
+ parse-settings-reference:
441
+ default: apic-default-parsesettings
442
+ ```
443
+
444
+ ```yaml
445
+ definitions:
446
+ Request:
447
+ type: object
448
+ properties:
449
+ <field>:
450
+ type: string # one per body field
451
+ example: >-
452
+ { "<field>": "<sample>" }
453
+ ```
454
+
455
+ ---
456
+
457
+ ## Policy variations reference (drawn from the three live samples)
458
+
459
+ Use these exact forms when the prompt calls for them. The three samples map to:
460
+ **AhelApp** = POST + leading parse + request map + logging(v2.3.0) + envelope in-case;
461
+ **Muamalaty** = GET + logging(v2.0.0) + Token-header backend + forwarded headers + envelope top-level;
462
+ **AmerApp** = GET + NO logging + verb keep + backend-type detect + named parse + envelope in-case.
463
+
464
+ ### Logging invoke — two variants (omit entirely if `--no-logging`, AmerApp)
465
+
466
+ **(a) v2.0.0 "json" (Muamalaty)** — current skeleton default:
467
+ ```yaml
468
+ - invoke:
469
+ version: 2.0.0
470
+ title: Log-invoke
471
+ verb: POST
472
+ target-url: >-
473
+ $(sakani-mw-base-url)/MW/MWLogServices/GeneralLog/UpsertMDLogQ?Environment=$(env.path)&ServiceName=$(api.name)&UserName=$(client.app.id)&RequestDt=$(system.datetime)
474
+ follow-redirects: false
475
+ timeout: 60
476
+ parameter-control: { type: allowlist, values: [] }
477
+ header-control: { type: blocklist, values: [] }
478
+ inject-proxy-headers: true
479
+ persistent-connection: true
480
+ backend-type: json
481
+ output: outResp
482
+ keep-payload: true
483
+ chunked-uploads: true
484
+ ```
485
+ **(b) v2.3.0 "detect" (AhelApp)** — preceded by its own `set-variable` setting `Authorization: $(nhc-mw-auth)`:
486
+ ```yaml
487
+ - invoke:
488
+ version: 2.3.0
489
+ title: log-invoke
490
+ backend-type: detect
491
+ header-control: { type: blocklist, values: [] }
492
+ parameter-control: { type: allowlist, values: [] }
493
+ http-version: HTTP/1.1
494
+ timeout: 60
495
+ verb: POST
496
+ chunked-uploads: true
497
+ persistent-connection: true
498
+ cache-response: protocol
499
+ cache-ttl: 900
500
+ stop-on-error: []
501
+ websocket-upgrade: false
502
+ graphql-send-type: detect
503
+ target-url: >-
504
+ $(sakani-mw-base-url)/MW/MWLogServices/GeneralLog/UpsertMDLogQ?Environment=$(env.path)&ServiceName=$(api.name)&UserName=$(client.app.id)&RequestDt=$(system.datetime)
505
+ output: logResp
506
+ ```
507
+
508
+ ### Backend invoke — v2.3.0 / detect / keep (AmerApp)
509
+ Same as the skeleton invoke, but with these fields:
510
+ ```yaml
511
+ - invoke:
512
+ version: 2.3.0
513
+ title: invoke
514
+ backend-type: detect
515
+ header-control: { type: blocklist, values: [] }
516
+ parameter-control: { type: allowlist, values: [expand] }
517
+ http-version: HTTP/1.1
518
+ timeout: 60
519
+ verb: keep
520
+ chunked-uploads: true
521
+ persistent-connection: true
522
+ cache-response: protocol
523
+ cache-ttl: 900
524
+ stop-on-error: []
525
+ websocket-upgrade: false
526
+ graphql-send-type: detect
527
+ target-url: >-
528
+ $(erp-base-url)/hcmRestApi/resources/latest/workers?onlyData=true&q=emails.EmailAddress='$(decoded.claims.EmailAddress)'
529
+ ```
530
+
531
+ ### Backend with forwarded headers (Muamalaty) — Token auth + allowlist
532
+ ```yaml
533
+ - set-variable:
534
+ version: 2.0.0
535
+ title: set-variable
536
+ actions:
537
+ - set: message.headers.Token
538
+ value: $(nhc-cts-auth)
539
+ type: string
540
+ - invoke:
541
+ version: 2.2.0
542
+ title: invoke
543
+ backend-type: json
544
+ header-control:
545
+ type: allowlist
546
+ values: [Token, rolename]
547
+ parameter-control:
548
+ type: allowlist
549
+ values: [inboxId, index, pageSize, language, userEmail]
550
+ timeout: 60
551
+ verb: GET
552
+ # ...rest as skeleton...
553
+ target-url: >-
554
+ $(nhc-cts-url)/CTSIPADUtilities/action/GetCorrespondences?userEmail=$(decoded.claims.EmailAddress)
555
+ ```
556
+
557
+ ### Multi-action set-variable (AmerApp) — auth + static framework header
558
+ ```yaml
559
+ - set-variable:
560
+ version: 2.0.0
561
+ title: set-variable
562
+ actions:
563
+ - set: message.headers.Authorization
564
+ value: $(erp-ameruser-auth)
565
+ type: string
566
+ - set: message.headers.REST-Framework-Version
567
+ value: '4'
568
+ type: string
569
+ ```
570
+
571
+ ### Backend parse — named-default form (AmerApp)
572
+ ```yaml
573
+ - parse:
574
+ version: 2.2.0
575
+ title: parse
576
+ parse-settings-reference:
577
+ default: apic-default-parsesettings
578
+ use-content-type: true
579
+ ```
580
+
581
+ ### Envelope placement — top-level (Muamalaty)
582
+ The `Prepare Response Header` gatewayscript sits at **top-level execute, after the `switch`,
583
+ before `log`** — not inside the 200 case. The `switch` there is `version: 2.1.0`.
584
+
585
+ ### Quick per-sample knob table
586
+
587
+ | Knob | AhelApp | Muamalaty | AmerApp |
588
+ |---|---|---|---|
589
+ | method | POST | GET | GET |
590
+ | leading parse | yes | no | no |
591
+ | logging | v2.3.0 detect | v2.0.0 json | none |
592
+ | request map + definitions | yes | no | no |
593
+ | backend version / type | 2.2.0 / json | 2.2.0 / json | 2.3.0 / detect |
594
+ | backend verb | POST | GET | keep |
595
+ | backend auth header | Authorization $(servicenow-auth) | Token $(nhc-cts-auth) | Authorization $(erp-ameruser-auth) + REST-Framework-Version '4' |
596
+ | forwarded headers (allowlist) | — | Token, rolename | — |
597
+ | query params (allowlist) | — | inboxId,index,pageSize,language,userEmail | expand |
598
+ | backend parse | inline json | inline json (use-content-type) | default apic-default-parsesettings (use-content-type) |
599
+ | envelope placement | in-case | top-level | in-case |
600
+ | switch version | 2.0.0 | 2.1.0 | 2.0.0 |
601
+
602
+ ---
603
+
604
+ ## OpenAPI 3.0 transform (`--openapi 3.0`)
605
+
606
+ Keep `x-ibm-configuration` byte-identical. Transform **only the contract**:
607
+
608
+ | 2.0 | 3.0 |
609
+ |---|---|
610
+ | `swagger: '2.0'` | `openapi: 3.0.0` |
611
+ | `basePath: /v1/X` + `schemes: [https]` | `servers: [ { url: https://<host>/v1/X } ]` |
612
+ | `securityDefinitions:` | `components.securitySchemes:` |
613
+ | `definitions:` | `components.schemas:` |
614
+ | body param (`in: body`, `schema`) | `requestBody.content.application/json.schema` |
615
+ | response `schema:` | `responses.'200'.content.application/json.schema` |
616
+ | `$ref: '#/definitions/Request'` | `$ref: '#/components/schemas/Request'` |
617
+ | param `type: string` | param `schema: { type: string }` |
618
+
619
+ > Both versions are first-class outputs. Note only that the target APIC/DataPower instance must
620
+ > have OpenAPI 3.0 API support enabled to import a 3.0 file — a deployment fact for the user to
621
+ > confirm, not a reason for Orbit to prefer one version over the other.
622
+
623
+ ---
624
+
625
+ ## After generating
626
+
627
+ **Use the Write tool to create the file `{x-ibm-name}_{version}.yaml` in the current working
628
+ directory** (the directory Orbit was launched in). Do NOT merely print the YAML to the terminal —
629
+ the deliverable is a real `.yaml` file on disk. Write the full document, then print the summary
630
+ below referencing the path you wrote.
631
+
632
+ After the file is written, print a short summary:
633
+
634
+ ```
635
+ ✓ {ApiTitle} — APIC {2.0|3.0} definition generated → {x-ibm-name}_{version}.yaml
636
+
637
+ verb {VERB} {operation-path} (basePath {basePath})
638
+ JWT validate {on|off} crypto: $({crypto-object})
639
+ MW logging {on|off}
640
+ ESB envelope {on|off}
641
+ backend {backend-target-url}
642
+ request map {yes (N fields)|no}
643
+
644
+ Import into API Connect or publish to the DataPower API Gateway.
645
+ ```