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.
- package/LICENSE +29 -0
- package/README.md +326 -0
- package/_agent_reference/components/audit-logging.md +129 -0
- package/_agent_reference/components/common-utils.md +146 -0
- package/_agent_reference/components/error-handling.md +117 -0
- package/_agent_reference/components/http-adapters.md +134 -0
- package/_agent_reference/components/input-adapters.md +101 -0
- package/_agent_reference/components/output-adapters.md +124 -0
- package/_agent_reference/components/property-reader.md +100 -0
- package/_agent_reference/components/retry.md +107 -0
- package/_agent_reference/config/naming-conventions.md +188 -0
- package/_agent_reference/config/properties-template.md +158 -0
- package/_agent_reference/config/queue-definition.md +141 -0
- package/_agent_reference/esql-coding-standards.md +112 -0
- package/_agent_reference/msgflow-xml-reference.md +207 -0
- package/_agent_reference/node_types_extended.md +211 -0
- package/_agent_reference/templates/file-flow.md +212 -0
- package/_agent_reference/templates/http-flow.md +185 -0
- package/_agent_reference/templates/mq-flow.md +248 -0
- package/bin/import-specifier.mjs +13 -0
- package/bin/import-specifier.test.mjs +13 -0
- package/bin/orbit +32 -0
- package/dist/cli.mjs +492705 -0
- package/package.json +149 -0
- package/skills/ace-app-http/SKILL.md +523 -0
- package/skills/ace-app-mq/SKILL.md +554 -0
- package/skills/apic-api/SKILL.md +645 -0
|
@@ -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
|
+
```
|