@voxgig/apidef 8.11.0 → 8.12.1
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/apidef.d.ts +4 -1
- package/dist/apidef.js +12 -34
- package/dist/apidef.js.map +1 -1
- package/dist/builder/entity/entity.js +0 -33
- package/dist/builder/entity/entity.js.map +1 -1
- package/dist/builder/entity/info.js +0 -4
- package/dist/builder/entity/info.js.map +1 -1
- package/dist/builder/flow.d.ts +4 -1
- package/dist/builder/flow.js +36 -27
- package/dist/builder/flow.js.map +1 -1
- package/dist/guide/graphql01.js +0 -55
- package/dist/guide/graphql01.js.map +1 -1
- package/dist/guide/guide.js +3 -126
- package/dist/guide/guide.js.map +1 -1
- package/dist/guide/heuristic01.js +40 -181
- package/dist/guide/heuristic01.js.map +1 -1
- package/dist/parse/graphql.js +0 -25
- package/dist/parse/graphql.js.map +1 -1
- package/dist/parse.js +51 -56
- package/dist/parse.js.map +1 -1
- package/dist/resolved.d.ts +23 -0
- package/dist/resolved.js +92 -0
- package/dist/resolved.js.map +1 -0
- package/dist/resolver.js +0 -2
- package/dist/resolver.js.map +1 -1
- package/dist/transform/args.js +2 -25
- package/dist/transform/args.js.map +1 -1
- package/dist/transform/casecollide.d.ts +3 -0
- package/dist/transform/casecollide.js +67 -0
- package/dist/transform/casecollide.js.map +1 -0
- package/dist/transform/contract.js +43 -38
- package/dist/transform/contract.js.map +1 -1
- package/dist/transform/entity.js +0 -56
- package/dist/transform/entity.js.map +1 -1
- package/dist/transform/field.js +1 -368
- package/dist/transform/field.js.map +1 -1
- package/dist/transform/flowstep.js +0 -49
- package/dist/transform/flowstep.js.map +1 -1
- package/dist/transform/graphql.js +0 -36
- package/dist/transform/graphql.js.map +1 -1
- package/dist/transform/operation.js +0 -23
- package/dist/transform/operation.js.map +1 -1
- package/dist/transform/top.js +1 -128
- package/dist/transform/top.js.map +1 -1
- package/dist/transform.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types.d.ts +2 -0
- package/dist/types.js.map +1 -1
- package/dist/utility.d.ts +2 -1
- package/dist/utility.js +17 -316
- package/dist/utility.js.map +1 -1
- package/model/apidef.aon +20 -113
- package/model/guide.aon +1 -32
- package/package.json +5 -5
- package/src/apidef.ts +22 -33
- package/src/builder/entity/entity.ts +0 -33
- package/src/builder/entity/info.ts +0 -4
- package/src/builder/flow.ts +39 -24
- package/src/desc.ts +0 -22
- package/src/guide/graphql01.ts +0 -75
- package/src/guide/guide.ts +5 -127
- package/src/guide/heuristic01.ts +47 -182
- package/src/model.ts +6 -85
- package/src/parse/graphql.ts +0 -25
- package/src/parse.ts +61 -56
- package/src/resolved.ts +136 -0
- package/src/resolver.ts +0 -2
- package/src/transform/args.ts +2 -25
- package/src/transform/casecollide.ts +80 -0
- package/src/transform/contract.ts +44 -34
- package/src/transform/entity.ts +0 -56
- package/src/transform/field.ts +2 -368
- package/src/transform/flowstep.ts +0 -49
- package/src/transform/graphql.ts +0 -36
- package/src/transform/operation.ts +0 -23
- package/src/transform/top.ts +1 -131
- package/src/transform.ts +0 -1
- package/src/types.ts +4 -13
- package/src/utility.ts +18 -314
|
@@ -39,7 +39,6 @@ const METHOD_CONSIDER_ORDER = {
|
|
|
39
39
|
'OPTIONS': 700,
|
|
40
40
|
};
|
|
41
41
|
async function heuristic01(ctx) {
|
|
42
|
-
// TODO: Ordu needs better debug output to track task exec
|
|
43
42
|
const analysis = new ordu_1.Ordu({ select: { sort: true } }).add([
|
|
44
43
|
Prepare,
|
|
45
44
|
{
|
|
@@ -68,12 +67,6 @@ async function heuristic01(ctx) {
|
|
|
68
67
|
throw result.err;
|
|
69
68
|
}
|
|
70
69
|
const guide = result.data.guide;
|
|
71
|
-
// Reassign single-segment collection paths (e.g. "/people") onto the entity
|
|
72
|
-
// that owns the per-instance path ("/people/{id}"). Heuristic discovery can
|
|
73
|
-
// split the two when response schemas wrap the resource in a search/pagination
|
|
74
|
-
// component. Running this here — before base-guide.aontu is serialised —
|
|
75
|
-
// means the same merged layout flows into both guide-case (transformers off)
|
|
76
|
-
// and model-case (transformers on), so they agree on the final guide.
|
|
77
70
|
(0, entity_1.mergeCollectionPaths)(guide, ctx.log);
|
|
78
71
|
const metrics = guide.metrics;
|
|
79
72
|
const entities = Object.values(guide.entity);
|
|
@@ -138,7 +131,6 @@ function Prepare(spec) {
|
|
|
138
131
|
function MeasurePath(spec) {
|
|
139
132
|
const guide = spec.data.guide;
|
|
140
133
|
const metrics = guide.metrics;
|
|
141
|
-
// const pathstr = spec.node.key
|
|
142
134
|
const pathdef = spec.node.val;
|
|
143
135
|
metrics.count.path++;
|
|
144
136
|
metrics.count.method += ((pathdef.get ? 1 : 0) +
|
|
@@ -154,7 +146,6 @@ function MeasurePath(spec) {
|
|
|
154
146
|
function MeasureMethod(spec) {
|
|
155
147
|
const guide = spec.data.guide;
|
|
156
148
|
const metrics = guide.metrics;
|
|
157
|
-
// const methodstr = spec.node.key
|
|
158
149
|
const methoddef = spec.node.val;
|
|
159
150
|
const pathtags = methoddef.tags;
|
|
160
151
|
if (Array.isArray(pathtags)) {
|
|
@@ -206,30 +197,6 @@ function MeasureRef(spec) {
|
|
|
206
197
|
}
|
|
207
198
|
function selectAllMethods(_source, spec) {
|
|
208
199
|
const ctx = spec.ctx;
|
|
209
|
-
// const paths = ctx.def.paths
|
|
210
|
-
/*
|
|
211
|
-
let caught = capture(ctx.def, {
|
|
212
|
-
paths:
|
|
213
|
-
['`$SELECT`', /.* /,
|
|
214
|
-
['`$SELECT`', /^get|post|put|patch|delete$/i,
|
|
215
|
-
['`$APPEND`', 'methods', {
|
|
216
|
-
path: '`select$=key.paths`',
|
|
217
|
-
method: { '`$UPPER`': '`$KEY`' },
|
|
218
|
-
summary: '`.summary`',
|
|
219
|
-
tags: '`.tags`',
|
|
220
|
-
parameters: '`.parameters`',
|
|
221
|
-
responses: '`.responses`',
|
|
222
|
-
requestBody: '`.requestBody`'
|
|
223
|
-
}]
|
|
224
|
-
]
|
|
225
|
-
]
|
|
226
|
-
})
|
|
227
|
-
|
|
228
|
-
// TODO: capture should return these empty objects
|
|
229
|
-
caught = caught ?? {}
|
|
230
|
-
caught.methods = caught.methods ?? []
|
|
231
|
-
|
|
232
|
-
*/
|
|
233
200
|
let caught = { methods: [] };
|
|
234
201
|
for (const [path, pdef] of (0, utility_2.sortedEntries)(ctx.def.paths)) {
|
|
235
202
|
for (const [m, mdef] of (0, utility_2.sortedEntries)(pdef)) {
|
|
@@ -297,7 +264,6 @@ function ResolveEntityComponent(spec) {
|
|
|
297
264
|
return xref;
|
|
298
265
|
})
|
|
299
266
|
.filter(xref => null != xref.cmp)
|
|
300
|
-
// TODO: identify non - ent schemas
|
|
301
267
|
.filter(xref => !xref.val.includes('Meta'));
|
|
302
268
|
let cleanxrefs = cmpxrefs
|
|
303
269
|
.map(xref => {
|
|
@@ -430,19 +396,6 @@ function ResolveEntityName(spec) {
|
|
|
430
396
|
entname = 'entity' + work.entity.count.unresolved;
|
|
431
397
|
}
|
|
432
398
|
}
|
|
433
|
-
// A PATH SEGMENT CARRIES NO WORD BOUNDARIES, and the component name does.
|
|
434
|
-
//
|
|
435
|
-
// `/openbanking/payeeverification` is one lowercase run, so `canonize` has
|
|
436
|
-
// nothing to split on and the entity is named `payeeverification` ->
|
|
437
|
-
// `Payeeverification`. The boundary was never missing: the response
|
|
438
|
-
// component for that same operation is
|
|
439
|
-
// `PayeeVerification.PayeeVerificationResult`, which canonizes to
|
|
440
|
-
// `payee_verification_result`.
|
|
441
|
-
//
|
|
442
|
-
// Where the path wins over the component -- which is usually RIGHT, because
|
|
443
|
-
// a widely-reused response schema makes a poor entity name -- the component
|
|
444
|
-
// was being discarded whole, boundaries and all. Borrow just the boundaries
|
|
445
|
-
// back. See resplitFromCmp for why this cannot invent a name.
|
|
446
399
|
entname = (0, utility_2.resplitFromCmp)(entname, ment.cmp, why_path);
|
|
447
400
|
// Keep the pre-truncation name so a truncated-name collision can tell a
|
|
448
401
|
// re-encounter of the SAME origin (merge) from a genuinely different one
|
|
@@ -475,7 +428,10 @@ function ResolveEntityName(spec) {
|
|
|
475
428
|
work.pathowner = work.pathowner ?? {};
|
|
476
429
|
work.pathowner[pathStr] = work.pathowner[pathStr] ?? {};
|
|
477
430
|
work.pathowner[pathStr][methodName] = entname;
|
|
478
|
-
|
|
431
|
+
// Same guard, same reason: the formatting is the cost, not the call.
|
|
432
|
+
if ((0, utility_2.debugpathOn)()) {
|
|
433
|
+
(0, utility_2.debugpath)(pathStr, methodName, 'RESOLVE-ENTITY-NAME', (0, utility_2.formatJSONIC)({ entdesc, ment }, { hsepd: 0, $: true, color: true }));
|
|
434
|
+
}
|
|
479
435
|
}
|
|
480
436
|
function RenameParams(spec) {
|
|
481
437
|
const ctx = spec.ctx;
|
|
@@ -490,12 +446,6 @@ function RenameParams(spec) {
|
|
|
490
446
|
const entdesc = work.entmap[entname];
|
|
491
447
|
const pathdesc = spec.data.work.pathmap[pathStr];
|
|
492
448
|
const methodName = mdesc.method;
|
|
493
|
-
// Rewrite path parameters that are identifiers to follow the rules:
|
|
494
|
-
// 0. Parameters named [a-z]?id are considered identifiers
|
|
495
|
-
// 1. last identifier is always {id} as this is the primary entity
|
|
496
|
-
// 2. internal identifiers are formatted as {name_id} where name is the parent entity name
|
|
497
|
-
// Example: /api/bar/{id}/zed/{zid}/foo/{fid} ->
|
|
498
|
-
// /api/bar/{bar_id}/zed/{zed_id}/foo/{id}
|
|
499
449
|
const pathDesc = entdesc.path[pathStr];
|
|
500
450
|
pathDesc.rename = (pathDesc.rename ?? { param: {} });
|
|
501
451
|
pathDesc.why_rename = (pathDesc.why_rename ?? { why_param: {} });
|
|
@@ -506,12 +456,6 @@ function RenameParams(spec) {
|
|
|
506
456
|
why: pathDesc.why_rename.why_param = (pathDesc.why_rename.why_param ?? {}),
|
|
507
457
|
};
|
|
508
458
|
const parts = pathdesc.parts;
|
|
509
|
-
// Implicit snake_case normalization for any path placeholder not already
|
|
510
|
-
// renamed by the id-rename logic. apidef's args transform snake-cases param
|
|
511
|
-
// names (e.g. spec `platformKey` → param.name `platform_key`); without
|
|
512
|
-
// normalizing the placeholder to match, runtime URL substitution by
|
|
513
|
-
// param.name fails to fill `{platformKey}`. Defined as a closure so we can
|
|
514
|
-
// run it after the id-rename loop OR after the multi-param early-return.
|
|
515
459
|
const applySnakeCaseRename = () => {
|
|
516
460
|
for (const part of parts) {
|
|
517
461
|
const m = part.match(/^\{(.+)\}$/);
|
|
@@ -579,7 +523,6 @@ function RenameParams(spec) {
|
|
|
579
523
|
&& entdesc.name.startsWith(parentName + '_'))
|
|
580
524
|
// || parentName === cmp.name
|
|
581
525
|
|| parentName === cmpname)) {
|
|
582
|
-
// let newParamName = 'id'
|
|
583
526
|
updateParamRename(ctx, data, pathStr, methodName, paramRenameCapture, oldParam, 'id', 'action-parent:' + entdesc.name);
|
|
584
527
|
why.push('action');
|
|
585
528
|
updateAction(methodName, oldParam, parts[partI + 1], entdesc, pathDesc, 'action-not-parent');
|
|
@@ -636,8 +579,6 @@ function RenameParams(spec) {
|
|
|
636
579
|
&& parentName === cmpname) {
|
|
637
580
|
updateParamRename(ctx, data, pathStr, methodName, paramRenameCapture, oldParam, 'id', 'id-not-last');
|
|
638
581
|
why.push('id-not-last');
|
|
639
|
-
// paramRenames[oldParam] = 'id'
|
|
640
|
-
// paramRenamesWhy[oldParam].push('id-not-last')
|
|
641
582
|
}
|
|
642
583
|
// Not primary ent.
|
|
643
584
|
else {
|
|
@@ -646,8 +587,6 @@ function RenameParams(spec) {
|
|
|
646
587
|
if (newParamName != oldParam) {
|
|
647
588
|
updateParamRename(ctx, data, pathStr, methodName, paramRenameCapture, oldParam, newParamName, 'not-primary');
|
|
648
589
|
why.push('not-primary');
|
|
649
|
-
// paramRenames[oldParam] = newParamName
|
|
650
|
-
// paramRenamesWhy[oldParam].push('not-primary')
|
|
651
590
|
}
|
|
652
591
|
}
|
|
653
592
|
}
|
|
@@ -657,7 +596,6 @@ function RenameParams(spec) {
|
|
|
657
596
|
delete paramRenameCapture.rename[oldParam];
|
|
658
597
|
delete paramRenameCapture.why[oldParam];
|
|
659
598
|
}
|
|
660
|
-
// TODO: these need to done via an API
|
|
661
599
|
(0, utility_2.debugpath)(pathStr, methodName, 'RENAME-PARAM', {
|
|
662
600
|
pathStr,
|
|
663
601
|
methodName,
|
|
@@ -691,7 +629,6 @@ function FindActions(spec) {
|
|
|
691
629
|
const ment = mdesc.MethodEntity;
|
|
692
630
|
const entname = ment.entname;
|
|
693
631
|
const entdesc = work.entmap[entname];
|
|
694
|
-
// const pathdesc = spec.data.work.pathmap[pathStr]
|
|
695
632
|
const pathdesc = entdesc.path[pathStr];
|
|
696
633
|
const methodName = mdesc.method;
|
|
697
634
|
pathdesc.action = (pathdesc.action ?? {});
|
|
@@ -706,12 +643,6 @@ function FindActions(spec) {
|
|
|
706
643
|
const lastPart = parts[parts.length - 1];
|
|
707
644
|
const lastPartCanon = (0, utility_2.canonize)(lastPart);
|
|
708
645
|
const cmp = ment.cmp;
|
|
709
|
-
// A verb that ResolveEntityName assigned to its parent entity
|
|
710
|
-
// (verbOnParent) is an action whatever the parent literal canonizes to:
|
|
711
|
-
// `/app/installations/{installation_id}/access_tokens` belongs to `app`.
|
|
712
|
-
// Recorded directly rather than through updateAction, whose guard against
|
|
713
|
-
// an entity "already encoding" the verb would drop `archive` on
|
|
714
|
-
// `email_archive` and leave the verb as a plain CRUD point.
|
|
715
646
|
if (null != ment.verb_on_parent) {
|
|
716
647
|
pathdesc.action[lastPartCanon] = pathdesc.action[lastPartCanon] ?? {
|
|
717
648
|
why_action: ['ent', entdesc.name, 'verb-on-parent', lastPart, methodName],
|
|
@@ -762,39 +693,13 @@ function ResolveOperation(spec) {
|
|
|
762
693
|
why_op.push('no-op:' + methodName);
|
|
763
694
|
return;
|
|
764
695
|
}
|
|
765
|
-
// REVIEW: using POST and PUT in non-restian ways is too wierd to handle consistently
|
|
766
|
-
// correct using guide customizations
|
|
767
|
-
// Sometimes POST is used to update, not create. Attempt to identify this.
|
|
768
|
-
// And sometimes vice versa for PUT
|
|
769
|
-
// const id_param_offset = ment.pm?.expr?.endsWith('/t/') ? 1 : 0
|
|
770
|
-
// const has_end_id_param =
|
|
771
|
-
// entname == canonize(parts[parts.length - 2 - id_param_offset])
|
|
772
|
-
// && parts[parts.length - 1 - id_param_offset]?.toLowerCase().endsWith('id}')
|
|
773
696
|
if ('load' === standard_opname) {
|
|
774
697
|
const islist = isListResponse(mdesc, pathStr, why_op);
|
|
775
698
|
opname = islist ? 'list' : opname;
|
|
776
699
|
}
|
|
777
|
-
/*
|
|
778
|
-
else if (
|
|
779
|
-
'create' === standard_opname
|
|
780
|
-
&& has_end_id_param
|
|
781
|
-
) {
|
|
782
|
-
opname = 'update'
|
|
783
|
-
why_op.push('id-present')
|
|
784
|
-
}
|
|
785
|
-
|
|
786
|
-
else if (
|
|
787
|
-
'update' === standard_opname
|
|
788
|
-
&& !has_end_id_param
|
|
789
|
-
) {
|
|
790
|
-
opname = 'create'
|
|
791
|
-
why_op.push('no-id-present')
|
|
792
|
-
}
|
|
793
|
-
*/
|
|
794
700
|
else {
|
|
795
701
|
why_op.push('not-load');
|
|
796
702
|
}
|
|
797
|
-
// why.push('ent=' + entdesc.name)
|
|
798
703
|
ment.opname = opname;
|
|
799
704
|
ment.why_opname = why_op;
|
|
800
705
|
// Tally access-token exchanges per entity. An entity whose ops are ALL
|
|
@@ -829,7 +734,6 @@ function ResolveTransform(spec) {
|
|
|
829
734
|
const work = spec.data.work;
|
|
830
735
|
const entname = mdesc.MethodEntity.entname;
|
|
831
736
|
const entdesc = work.entmap[entname];
|
|
832
|
-
// const pathdesc = spec.data.work.pathmap[pathStr]
|
|
833
737
|
const pathdesc = entdesc.path[pathStr];
|
|
834
738
|
const methodName = mdesc.method;
|
|
835
739
|
const opname = ment.opname;
|
|
@@ -843,12 +747,6 @@ function ResolveTransform(spec) {
|
|
|
843
747
|
const resprops = getResponseSchema(resokdef)?.properties;
|
|
844
748
|
(0, utility_2.debugpath)(pathStr, methodName, 'TRANSFORM-RES', (0, struct_1.keysof)(resprops));
|
|
845
749
|
if (resprops) {
|
|
846
|
-
// Only unwrap `body.<entity>` when the entity-named response property is
|
|
847
|
-
// itself a structured value (object/array/ref/composed schema) that could
|
|
848
|
-
// actually contain the entity. A scalar property that merely shares the
|
|
849
|
-
// entity's name (e.g. an entity `advice` whose own fields include a
|
|
850
|
-
// string field `advice`) is a FIELD of the entity, not a wrapper around
|
|
851
|
-
// it: the response IS the entity, so it must stay `body` (the default).
|
|
852
750
|
if ((0, utility_1.isEntityWrapperProp)(resprops[entdesc.origname])) {
|
|
853
751
|
transform.res = '`body.' + entdesc.origname + '`';
|
|
854
752
|
}
|
|
@@ -867,14 +765,6 @@ function ResolveTransform(spec) {
|
|
|
867
765
|
}
|
|
868
766
|
}
|
|
869
767
|
}
|
|
870
|
-
// The SCHEMA is what closedBodyTransform needs (it reads
|
|
871
|
-
// additionalProperties); the wrapper-name checks need its PROPERTIES. They
|
|
872
|
-
// used to share one value and index the schema itself, so
|
|
873
|
-
// `schema['todoitem']` was always undefined and the entity-name request
|
|
874
|
-
// envelope was never detected — while the Go port read `.properties` and
|
|
875
|
-
// did detect it. That divergence was inert only because `req` was never
|
|
876
|
-
// serialised; now that it is, the two implementations would emit different
|
|
877
|
-
// request bodies for the same spec.
|
|
878
768
|
const reqschema = getRequestBodySchema(mdesc.requestBody);
|
|
879
769
|
const reqprops = reqschema?.properties;
|
|
880
770
|
(0, utility_2.debugpath)(pathStr, methodName, 'TRANSFORM-REQ', (0, struct_1.keysof)(reqprops));
|
|
@@ -933,12 +823,6 @@ function BuildEntity(spec) {
|
|
|
933
823
|
orig: entdesc.origcmp,
|
|
934
824
|
path,
|
|
935
825
|
};
|
|
936
|
-
// An entity built ENTIRELY out of access-token exchanges is not a resource
|
|
937
|
-
// — it is the credential plumbing an SDK's auth layer performs. Emitted
|
|
938
|
-
// with `active: false` rather than dropped, so it stays visible in
|
|
939
|
-
// guide.aon and the classification can be reversed there (ADR-002): flip
|
|
940
|
-
// it to `true` and the entity comes back. The exchange itself is recorded
|
|
941
|
-
// separately as model facts by transform/top.ts.
|
|
942
826
|
if (0 < entdesc.authexchange_ops && entdesc.authexchange_ops === entdesc.total_ops) {
|
|
943
827
|
guideEntity.active = false;
|
|
944
828
|
guideEntity.why_inactive = 'auth-exchange';
|
|
@@ -995,28 +879,6 @@ function endsWithCmp(data, pm) {
|
|
|
995
879
|
const last = (0, utility_2.canonize)((0, struct_1.getelem)(pm, -1));
|
|
996
880
|
return isOrigCmp(data, last);
|
|
997
881
|
}
|
|
998
|
-
// A write on `.../<parent>/{id}/<verb>` is a VERB ON THE PARENT, not an
|
|
999
|
-
// entity named after its result shape.
|
|
1000
|
-
//
|
|
1001
|
-
// GitHub's `PUT /repos/{owner}/{repo}/pulls/{pull_number}/merge` answers with
|
|
1002
|
-
// a `pull-request-merge-result` component. Naming the method's entity after
|
|
1003
|
-
// that component (the cmp-primary rule) produced a `pull_request_merge_result`
|
|
1004
|
-
// entity with a single `update` op, while the GET on the same path (no
|
|
1005
|
-
// response schema) stayed an action on `pull`: one route split across two
|
|
1006
|
-
// entities by method, and the verb unreachable from the entity it acts on.
|
|
1007
|
-
//
|
|
1008
|
-
// Five signals, together: the method writes (a GET on such a path is a
|
|
1009
|
-
// sub-resource read and keeps the component rule); the response component
|
|
1010
|
-
// occurs nowhere else in the spec (a one-off result, not a resource shape);
|
|
1011
|
-
// that component is not the literal's own collection shape (a create-only
|
|
1012
|
-
// `POST .../{id}/labels` answering with a `label` is a nested collection,
|
|
1013
|
-
// not a verb); the item selector itself (`.../pulls/{pull_number}`) is a
|
|
1014
|
-
// path of the spec, so the trailing literal cannot be a collection of its
|
|
1015
|
-
// own; and nothing extends the path (`.../private-registries/{secret_name}`
|
|
1016
|
-
// makes `private-registries` a collection, whatever its POST answers with).
|
|
1017
|
-
// The verb then joins the parent entity, where FindActions records it as an
|
|
1018
|
-
// action and select stamps `$action` on its points. Returns the parent's
|
|
1019
|
-
// entity name, or null when the rule does not apply.
|
|
1020
882
|
function verbOnParent(data, pm, mdesc) {
|
|
1021
883
|
const method = mdesc.method;
|
|
1022
884
|
if ('GET' === method || 'QUERY' === method || 'HEAD' === method || 'OPTIONS' === method) {
|
|
@@ -1026,20 +888,10 @@ function verbOnParent(data, pm, mdesc) {
|
|
|
1026
888
|
if (1 < (ment.cmpoccur ?? 0)) {
|
|
1027
889
|
return null;
|
|
1028
890
|
}
|
|
1029
|
-
// A PLURAL literal names a nested collection, whatever it answers with:
|
|
1030
|
-
// `asset_keys` under `{environment_id}` creates an asset key, and
|
|
1031
|
-
// `approvals` under `{merge_request_iid}` is a collection of approvals.
|
|
1032
|
-
// A verb is singular — `merge`, `revoke`, `resend_confirmation` — so the
|
|
1033
|
-
// component rule below is never reached for a plural, which is what keeps
|
|
1034
|
-
// a create-only collection an entity of its own.
|
|
1035
891
|
const lit = (0, jostraca_2.snakify)((0, struct_1.getelem)(pm, -1));
|
|
1036
892
|
if ('' === lit || (0, utility_2.depluralize)(lit) !== lit) {
|
|
1037
893
|
return null;
|
|
1038
894
|
}
|
|
1039
|
-
// A singular literal still names a collection when its response component
|
|
1040
|
-
// is that collection's member shape (`label` answering with `label`, or
|
|
1041
|
-
// with a parent-prefixed `thing_label`); a verb answers with something
|
|
1042
|
-
// else.
|
|
1043
895
|
const verb = (0, utility_2.canonize)((0, struct_1.getelem)(pm, -1));
|
|
1044
896
|
const cmp = String(ment.cmp ?? '');
|
|
1045
897
|
if ('' === verb || cmp === verb || cmp.endsWith('_' + verb)) {
|
|
@@ -1089,7 +941,6 @@ function entityPathMatch_tpe(data, pm, mdesc, why) {
|
|
|
1089
941
|
const pathNameIndex = 0;
|
|
1090
942
|
why.push('path=t/p/');
|
|
1091
943
|
const origPathName = pm[pathNameIndex];
|
|
1092
|
-
// let entname = fixEntName(origPathName)
|
|
1093
944
|
let entname = (0, utility_2.canonize)(origPathName);
|
|
1094
945
|
if (null != ment.cmp || probableEntityMethod(data, mdesc, pm, why)) {
|
|
1095
946
|
let ecm = entityCmpMatch(data, entname, mdesc, why);
|
|
@@ -1120,7 +971,6 @@ function entityPathMatch_te(data, pm, mdesc, why) {
|
|
|
1120
971
|
const pathNameIndex = 0;
|
|
1121
972
|
why.push('path=t/');
|
|
1122
973
|
const origPathName = pm[pathNameIndex];
|
|
1123
|
-
// let entname = fixEntName(origPathName)
|
|
1124
974
|
let entname = (0, utility_2.canonize)(origPathName);
|
|
1125
975
|
if (null != ment.cmp || probableEntityMethod(data, mdesc, pm, why)) {
|
|
1126
976
|
let ecm = entityCmpMatch(data, entname, mdesc, why);
|
|
@@ -1136,7 +986,6 @@ function entityPathMatch_tpp(data, pm, mdesc, why) {
|
|
|
1136
986
|
const pathNameIndex = 0;
|
|
1137
987
|
why.push('path=t/p/p');
|
|
1138
988
|
const origPathName = pm[pathNameIndex];
|
|
1139
|
-
// let entname = fixEntName(origPathName)
|
|
1140
989
|
let entname = (0, utility_2.canonize)(origPathName);
|
|
1141
990
|
if (null != ment.cmp || probableEntityMethod(data, mdesc, pm, why)) {
|
|
1142
991
|
let ecm = entityCmpMatch(data, entname, mdesc, why);
|
|
@@ -1186,7 +1035,6 @@ function inferEntityName(mdesc, parts, why) {
|
|
|
1186
1035
|
}
|
|
1187
1036
|
return null;
|
|
1188
1037
|
}
|
|
1189
|
-
// No entity component was found, but there still might be an entity.
|
|
1190
1038
|
function probableEntityMethod(data, mdesc, pm, why) {
|
|
1191
1039
|
const request = mdesc.requestBody;
|
|
1192
1040
|
const reqSchema = getRequestBodySchema(request);
|
|
@@ -1300,14 +1148,6 @@ function isListResponse(mdesc, pathStr, why) {
|
|
|
1300
1148
|
const pm = ment.pm;
|
|
1301
1149
|
let islist = false;
|
|
1302
1150
|
let schema;
|
|
1303
|
-
// 'p/' is anchored (e.g. t/p/): the path ends at a param, so it is an
|
|
1304
|
-
// item path and the response shape cannot change that.
|
|
1305
|
-
//
|
|
1306
|
-
// A bare trailing 'p' (e.g. t/p/p, a compound key like
|
|
1307
|
-
// /repos/{owner}/{repo}) also ends at a param, but the same shape covers
|
|
1308
|
-
// a sub-collection scoped by a compound key (e.g. /audit-log/{ns}/{repo}).
|
|
1309
|
-
// Those are told apart by the response: a collection returns an array at
|
|
1310
|
-
// the top level, an item does not.
|
|
1311
1151
|
const endParamAnchored = !!(pm && pm.expr.endsWith('p/'));
|
|
1312
1152
|
const endParamBare = !!(pm && !endParamAnchored && pm.expr.endsWith('p'));
|
|
1313
1153
|
if (endParamAnchored) {
|
|
@@ -1324,11 +1164,6 @@ function isListResponse(mdesc, pathStr, why) {
|
|
|
1324
1164
|
why.push('array');
|
|
1325
1165
|
islist = true;
|
|
1326
1166
|
}
|
|
1327
|
-
// The array-prop fallback is deliberately loose, and an item schema
|
|
1328
|
-
// often carries an incidental array property (GitHub's full-repository
|
|
1329
|
-
// has topics: string[]). That is good enough evidence for an ordinary
|
|
1330
|
-
// path, but not for a compound-key path, where it is exactly what
|
|
1331
|
-
// misclassifies the item as a list.
|
|
1332
1167
|
if (!islist && !endParamBare) {
|
|
1333
1168
|
const properties = resolveSchemaProperties(schema);
|
|
1334
1169
|
(0, jostraca_1.each)(properties, (prop) => {
|
|
@@ -1371,6 +1206,42 @@ function updateAction(methodName, oldParam, actionName, entityDesc, pathdesc, wh
|
|
|
1371
1206
|
}
|
|
1372
1207
|
}
|
|
1373
1208
|
function updateParamRename(ctx, data, path, method, paramRenameCapture, oldParamName, newParamName, why) {
|
|
1209
|
+
// A name that cannot be an identifier is not an improvement on the one the
|
|
1210
|
+
// specification gave. See docs/design/derived-names.md
|
|
1211
|
+
if (!/^[A-Za-z_]/.test(newParamName)) {
|
|
1212
|
+
ctx.log.debug({
|
|
1213
|
+
point: 'param-rename-skip',
|
|
1214
|
+
path,
|
|
1215
|
+
param: oldParamName,
|
|
1216
|
+
rejected: newParamName,
|
|
1217
|
+
note: 'derived parameter name is not an identifier, keeping the' +
|
|
1218
|
+
" specification's name"
|
|
1219
|
+
});
|
|
1220
|
+
return;
|
|
1221
|
+
}
|
|
1222
|
+
// The clash is with what the path's OTHER parameters end up called: their
|
|
1223
|
+
// rename if they have one, their canonical name if not.
|
|
1224
|
+
const otherParams = (path.match(/\{([^}]+)\}/g) || [])
|
|
1225
|
+
.map((seg) => seg.slice(1, -1))
|
|
1226
|
+
.filter((name) => name !== oldParamName);
|
|
1227
|
+
const takenBy = Object.keys(paramRenameCapture.rename)
|
|
1228
|
+
.find((other) => other !== oldParamName &&
|
|
1229
|
+
paramRenameCapture.rename[other] === newParamName) ??
|
|
1230
|
+
otherParams
|
|
1231
|
+
.find((other) => null == paramRenameCapture.rename[other] &&
|
|
1232
|
+
(0, utility_2.canonize)(other) === newParamName);
|
|
1233
|
+
if (null != takenBy) {
|
|
1234
|
+
ctx.log.debug({
|
|
1235
|
+
point: 'param-rename-collision',
|
|
1236
|
+
path,
|
|
1237
|
+
param: oldParamName,
|
|
1238
|
+
rejected: newParamName,
|
|
1239
|
+
takenBy,
|
|
1240
|
+
note: 'another parameter of this path already renames to ' +
|
|
1241
|
+
newParamName + ", keeping the specification's name"
|
|
1242
|
+
});
|
|
1243
|
+
return;
|
|
1244
|
+
}
|
|
1374
1245
|
const existingNewName = paramRenameCapture.rename[oldParamName];
|
|
1375
1246
|
const existingWhy = paramRenameCapture.why[oldParamName];
|
|
1376
1247
|
(0, utility_2.debugpath)(path, method, 'UPDATE-PARAM-RENAME', path, oldParamName, newParamName, existingNewName);
|
|
@@ -1381,9 +1252,6 @@ function updateParamRename(ctx, data, path, method, paramRenameCapture, oldParam
|
|
|
1381
1252
|
}
|
|
1382
1253
|
}
|
|
1383
1254
|
else if (newParamName == existingNewName) {
|
|
1384
|
-
// if (!existingWhy.includes(why)) {
|
|
1385
|
-
// existingWhy.push(why)
|
|
1386
|
-
// }
|
|
1387
1255
|
}
|
|
1388
1256
|
else {
|
|
1389
1257
|
ctx.warn({
|
|
@@ -1398,18 +1266,9 @@ function updateParamRename(ctx, data, path, method, paramRenameCapture, oldParam
|
|
|
1398
1266
|
function isParam(partStr) {
|
|
1399
1267
|
return '{' === partStr[0] && '}' === partStr[partStr.length - 1];
|
|
1400
1268
|
}
|
|
1401
|
-
/*
|
|
1402
|
-
function fixEntName(origName: string) {
|
|
1403
|
-
if (null == origName) {
|
|
1404
|
-
return origName
|
|
1405
|
-
}
|
|
1406
|
-
return depluralize(snakify(origName))
|
|
1407
|
-
}
|
|
1408
|
-
*/
|
|
1409
1269
|
function findcmps(data, pathStr, underprops, opts) {
|
|
1410
1270
|
const cmplist = [];
|
|
1411
1271
|
const cmpset = new Set();
|
|
1412
|
-
// TODO: cache in ctx.work
|
|
1413
1272
|
(0, jostraca_1.each)(data.def.paths[pathStr])
|
|
1414
1273
|
.map((md) => {
|
|
1415
1274
|
underprops.map((up) => {
|