@voxgig/apidef 2.4.0 → 3.0.2
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 +5 -1
- package/dist/apidef.js +197 -112
- package/dist/apidef.js.map +1 -1
- package/dist/builder/entity/entity.d.ts +3 -0
- package/dist/builder/entity/{apiEntity.js → entity.js} +12 -9
- package/dist/builder/entity/entity.js.map +1 -0
- package/dist/builder/entity/info.d.ts +3 -0
- package/dist/builder/entity/info.js +22 -0
- package/dist/builder/entity/info.js.map +1 -0
- package/dist/builder/entity.js +7 -21
- package/dist/builder/entity.js.map +1 -1
- package/dist/builder/flow/flowHeuristic01.js +21 -11
- package/dist/builder/flow/flowHeuristic01.js.map +1 -1
- package/dist/builder/flow.d.ts +2 -1
- package/dist/builder/flow.js +29 -4
- package/dist/builder/flow.js.map +1 -1
- package/dist/def.d.ts +62 -0
- package/dist/def.js +4 -0
- package/dist/def.js.map +1 -0
- package/dist/desc.d.ts +89 -0
- package/dist/desc.js +4 -0
- package/dist/desc.js.map +1 -0
- package/dist/guide/guide.d.ts +2 -1
- package/dist/guide/guide.js +161 -30
- package/dist/guide/guide.js.map +1 -1
- package/dist/guide/heuristic01.d.ts +2 -1
- package/dist/guide/heuristic01.js +1120 -234
- package/dist/guide/heuristic01.js.map +1 -1
- package/dist/model.d.ts +55 -0
- package/dist/model.js +4 -0
- package/dist/model.js.map +1 -0
- package/dist/parse.d.ts +1 -2
- package/dist/parse.js +8 -47
- package/dist/parse.js.map +1 -1
- package/dist/transform/args.d.ts +3 -0
- package/dist/transform/args.js +58 -0
- package/dist/transform/args.js.map +1 -0
- package/dist/transform/clean.js +27 -3
- package/dist/transform/clean.js.map +1 -1
- package/dist/transform/entity.d.ts +11 -3
- package/dist/transform/entity.js +57 -41
- package/dist/transform/entity.js.map +1 -1
- package/dist/transform/field.d.ts +3 -3
- package/dist/transform/field.js +90 -65
- package/dist/transform/field.js.map +1 -1
- package/dist/transform/operation.d.ts +1 -1
- package/dist/transform/operation.js +94 -296
- package/dist/transform/operation.js.map +1 -1
- package/dist/transform/select.d.ts +3 -0
- package/dist/transform/select.js +44 -0
- package/dist/transform/select.js.map +1 -0
- package/dist/transform/top.d.ts +9 -0
- package/dist/transform/top.js +11 -2
- package/dist/transform/top.js.map +1 -1
- package/dist/transform.js +4 -0
- package/dist/transform.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types.d.ts +112 -19
- package/dist/types.js +4 -2
- package/dist/types.js.map +1 -1
- package/dist/utility.d.ts +30 -2
- package/dist/utility.js +381 -6
- package/dist/utility.js.map +1 -1
- package/model/apidef.jsonic +75 -1
- package/model/guide.jsonic +14 -44
- package/package.json +19 -14
- package/src/apidef.ts +264 -121
- package/src/builder/entity/{apiEntity.ts → entity.ts} +18 -11
- package/src/builder/entity/info.ts +53 -0
- package/src/builder/entity.ts +9 -35
- package/src/builder/flow/flowHeuristic01.ts +46 -12
- package/src/builder/flow.ts +39 -5
- package/src/def.ts +91 -0
- package/src/desc.ts +143 -0
- package/src/guide/guide.ts +207 -134
- package/src/guide/heuristic01.ts +1651 -272
- package/src/model.ts +98 -0
- package/src/parse.ts +5 -61
- package/src/schematron.ts.off +317 -0
- package/src/transform/args.ts +102 -0
- package/src/transform/clean.ts +43 -8
- package/src/transform/entity.ts +100 -51
- package/src/transform/field.ts +150 -71
- package/src/transform/operation.ts +118 -414
- package/src/transform/select.ts +90 -0
- package/src/transform/top.ts +76 -3
- package/src/transform.ts +4 -0
- package/src/types.ts +185 -5
- package/src/utility.ts +481 -9
- package/dist/builder/entity/apiEntity.d.ts +0 -3
- package/dist/builder/entity/apiEntity.js.map +0 -1
- package/dist/builder/entity/def.d.ts +0 -3
- package/dist/builder/entity/def.js +0 -19
- package/dist/builder/entity/def.js.map +0 -1
- package/src/builder/entity/def.ts +0 -44
- package/src/guide.ts.off +0 -136
|
@@ -1,57 +1,188 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.heuristic01 = heuristic01;
|
|
4
|
+
const ordu_1 = require("ordu");
|
|
4
5
|
const jostraca_1 = require("jostraca");
|
|
5
6
|
const struct_1 = require("@voxgig/struct");
|
|
6
7
|
const utility_1 = require("../utility");
|
|
7
|
-
// Log non-fatal wierdness.
|
|
8
|
+
// Log non - fatal wierdness.
|
|
8
9
|
const dlog = (0, utility_1.getdlog)('apidef', __filename);
|
|
10
|
+
// Schema components that occur less than this rate(over total method count) qualify
|
|
11
|
+
// as unique entities, not shared schemas
|
|
12
|
+
const IS_ENTCMP_METHOD_RATE = 0.21;
|
|
13
|
+
const IS_ENTCMP_PATH_RATE = 0.41;
|
|
14
|
+
const METHOD_IDOP = {
|
|
15
|
+
GET: 'load',
|
|
16
|
+
POST: 'create',
|
|
17
|
+
PUT: 'update',
|
|
18
|
+
DELETE: 'remove',
|
|
19
|
+
PATCH: 'patch',
|
|
20
|
+
HEAD: 'head',
|
|
21
|
+
OPTIONS: 'OPTIONS',
|
|
22
|
+
};
|
|
23
|
+
const METHOD_CONSIDER_ORDER = {
|
|
24
|
+
'GET': 100,
|
|
25
|
+
'POST': 200,
|
|
26
|
+
'PUT': 300,
|
|
27
|
+
'PATCH': 400,
|
|
28
|
+
'DELETE': 500,
|
|
29
|
+
'HEAD': 600,
|
|
30
|
+
'OPTIONS': 700,
|
|
31
|
+
};
|
|
9
32
|
async function heuristic01(ctx) {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
33
|
+
const analysis = new ordu_1.Ordu({ select: { sort: true } }).add([
|
|
34
|
+
Prepare,
|
|
35
|
+
{
|
|
36
|
+
select: 'def.paths', apply: [
|
|
37
|
+
MeasurePath,
|
|
38
|
+
{ select: '', apply: MeasureMethod },
|
|
39
|
+
PreparePath
|
|
40
|
+
]
|
|
41
|
+
},
|
|
42
|
+
{ select: selectCmpXrefs, apply: MeasureRef },
|
|
43
|
+
{
|
|
44
|
+
select: selectAllMethods, apply: [
|
|
45
|
+
ResolveEntityComponent,
|
|
46
|
+
ResolveEntityName,
|
|
47
|
+
RenameParams,
|
|
48
|
+
FindActions,
|
|
49
|
+
ResolveOperation,
|
|
50
|
+
ResolveTransform,
|
|
51
|
+
// ShowNode,
|
|
52
|
+
]
|
|
53
|
+
},
|
|
54
|
+
{ select: 'work.entmap', apply: BuildEntity }
|
|
55
|
+
]);
|
|
56
|
+
const result = analysis.execSync(ctx, {});
|
|
57
|
+
if (result.err) {
|
|
58
|
+
throw result.err;
|
|
59
|
+
}
|
|
60
|
+
const guide = result.data.guide;
|
|
61
|
+
// console.log('WORK', result.data.work)
|
|
62
|
+
// console.log('GUIDE')
|
|
63
|
+
// console.dir(guide, { depth: null })
|
|
64
|
+
// TODO: move to Ordu
|
|
65
|
+
// warnOnError('reviewEntityDescs', ctx.warn, () => reviewEntityDescs(ctx, result))
|
|
18
66
|
return guide;
|
|
19
67
|
}
|
|
20
|
-
function
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
68
|
+
function ShowNode(spec) {
|
|
69
|
+
console.log('NODE', spec.node.key, spec.node.val);
|
|
70
|
+
}
|
|
71
|
+
function Prepare(spec) {
|
|
72
|
+
const guide = {
|
|
73
|
+
control: {},
|
|
74
|
+
entity: {},
|
|
75
|
+
metrics: {
|
|
76
|
+
count: {
|
|
77
|
+
path: 0,
|
|
78
|
+
method: 0,
|
|
79
|
+
tag: 0,
|
|
80
|
+
cmp: 0,
|
|
81
|
+
entity: 0,
|
|
82
|
+
origcmprefs: {},
|
|
83
|
+
},
|
|
84
|
+
found: {
|
|
85
|
+
tag: {},
|
|
86
|
+
cmp: {},
|
|
87
|
+
}
|
|
88
|
+
},
|
|
25
89
|
};
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
90
|
+
Object.assign(spec.data, {
|
|
91
|
+
def: spec.ctx.def,
|
|
92
|
+
guide,
|
|
93
|
+
work: {
|
|
94
|
+
pathmap: {},
|
|
95
|
+
entmap: {},
|
|
96
|
+
entity: {
|
|
97
|
+
count: {
|
|
98
|
+
seen: 0,
|
|
99
|
+
unresolved: 0,
|
|
100
|
+
}
|
|
101
|
+
},
|
|
34
102
|
}
|
|
35
103
|
});
|
|
36
|
-
return metrics;
|
|
37
104
|
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
105
|
+
// Expects to run over paths
|
|
106
|
+
function MeasurePath(spec) {
|
|
107
|
+
const guide = spec.data.guide;
|
|
108
|
+
const metrics = guide.metrics;
|
|
109
|
+
// const pathstr = spec.node.key
|
|
110
|
+
const pathdef = spec.node.val;
|
|
111
|
+
metrics.count.path++;
|
|
112
|
+
metrics.count.method += ((pathdef.get ? 1 : 0) +
|
|
113
|
+
(pathdef.post ? 1 : 0) +
|
|
114
|
+
(pathdef.put ? 1 : 0) +
|
|
115
|
+
(pathdef.patch ? 1 : 0) +
|
|
116
|
+
(pathdef.delete ? 1 : 0) +
|
|
117
|
+
(pathdef.head ? 1 : 0) +
|
|
118
|
+
(pathdef.options ? 1 : 0));
|
|
119
|
+
}
|
|
120
|
+
// Expects to run over paths.<method>
|
|
121
|
+
function MeasureMethod(spec) {
|
|
122
|
+
const guide = spec.data.guide;
|
|
123
|
+
const metrics = guide.metrics;
|
|
124
|
+
// const methodstr = spec.node.key
|
|
125
|
+
const methoddef = spec.node.val;
|
|
126
|
+
const pathtags = methoddef.tags;
|
|
127
|
+
if (Array.isArray(pathtags)) {
|
|
128
|
+
for (let tag of pathtags) {
|
|
129
|
+
if ('string' === typeof tag && 0 < tag.length) {
|
|
130
|
+
if (!metrics.found.tag[tag]) {
|
|
131
|
+
metrics.count.tag++;
|
|
132
|
+
metrics.found.tag[tag] = {
|
|
133
|
+
name: tag,
|
|
134
|
+
canon: (0, utility_1.canonize)(tag),
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
function PreparePath(spec) {
|
|
142
|
+
const work = spec.data.work;
|
|
143
|
+
const pathstr = spec.node.key;
|
|
144
|
+
const pathdef = spec.node.val;
|
|
145
|
+
const pathdesc = {
|
|
146
|
+
path: pathstr,
|
|
147
|
+
def: pathdef,
|
|
148
|
+
parts: pathstr.split('/').filter((p) => '' != p),
|
|
149
|
+
op: {}
|
|
150
|
+
};
|
|
151
|
+
work.pathmap[pathstr] = pathdesc;
|
|
152
|
+
}
|
|
153
|
+
function selectCmpXrefs(_source, spec) {
|
|
154
|
+
const out = (0, utility_1.find)(spec.ctx.def, 'x-ref')
|
|
155
|
+
.filter(xref => xref.val.match(/\/(components\/schemas|definitions)\//));
|
|
156
|
+
// console.log('selectCmpXrefs', out)
|
|
157
|
+
return out;
|
|
158
|
+
}
|
|
159
|
+
function MeasureRef(spec) {
|
|
160
|
+
const guide = spec.data.guide;
|
|
161
|
+
const metrics = guide.metrics;
|
|
162
|
+
let m = spec.node.val.val.match(/\/(components\/schemas|definitions)\/(.+)$/);
|
|
163
|
+
if (m) {
|
|
164
|
+
const name = (0, utility_1.canonize)(m[2]);
|
|
165
|
+
if (null == metrics.count.origcmprefs[name]) {
|
|
166
|
+
metrics.count.cmp++;
|
|
167
|
+
metrics.count.origcmprefs[name] = 0;
|
|
168
|
+
}
|
|
169
|
+
metrics.count.origcmprefs[name]++;
|
|
170
|
+
if (null == metrics.found.cmp[name]) {
|
|
171
|
+
metrics.found.cmp[name] = { orig: m[2] };
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
function selectAllMethods(_source, spec) {
|
|
176
|
+
const ctx = spec.ctx;
|
|
177
|
+
// const paths = ctx.def.paths
|
|
178
|
+
let caught = (0, utility_1.capture)(ctx.def, {
|
|
179
|
+
paths: ['`$SELECT`', /.*/,
|
|
180
|
+
['`$SELECT`', /^get|post|put|patch|delete$/i,
|
|
51
181
|
['`$APPEND`', 'methods', {
|
|
52
182
|
path: '`select$=key.paths`',
|
|
53
|
-
method: { '`$
|
|
183
|
+
method: { '`$UPPER`': '`$KEY`' },
|
|
54
184
|
summary: '`.summary`',
|
|
185
|
+
tags: '`.tags`',
|
|
55
186
|
parameters: '`.parameters`',
|
|
56
187
|
responses: '`.responses`',
|
|
57
188
|
requestBody: '`.requestBody`'
|
|
@@ -59,239 +190,994 @@ function resolveEntityDescs(ctx) {
|
|
|
59
190
|
]
|
|
60
191
|
]
|
|
61
192
|
});
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
if (!METHOD_IDOP[methodStr]) {
|
|
69
|
-
return;
|
|
70
|
-
}
|
|
71
|
-
const why_ent = [];
|
|
72
|
-
const entdesc = resolveEntity(entityDescs, pathStr, methodDef, methodStr, why_ent);
|
|
73
|
-
if (null == entdesc) {
|
|
74
|
-
console.log('WARNING: unable to resolve entity for method ' + methodStr +
|
|
75
|
-
' path ' + pathStr);
|
|
76
|
-
return;
|
|
77
|
-
}
|
|
78
|
-
entdesc.path[pathStr].why_ent = why_ent;
|
|
79
|
-
// if (pathStr.includes('courses')) {
|
|
80
|
-
// console.log('ENTRES', pathStr, methodStr)
|
|
81
|
-
// console.dir(ent2, { depth: null })
|
|
82
|
-
// }
|
|
83
|
-
let opname = resolveOpName(methodStr, methodDef, pathStr, entdesc, why_op);
|
|
84
|
-
if (null == opname) {
|
|
85
|
-
console.log('WARNING: unable to resolve operation for method ' + methodStr +
|
|
86
|
-
' path ' + pathStr);
|
|
87
|
-
return;
|
|
88
|
-
}
|
|
89
|
-
const transform = {
|
|
90
|
-
// reqform: '`reqdata`',
|
|
91
|
-
// resform: '`body`',
|
|
92
|
-
};
|
|
93
|
-
const resokdef = methodDef.responses?.[200] || methodDef.responses?.[201];
|
|
94
|
-
const resbody = resokdef?.content?.['application/json']?.schema;
|
|
95
|
-
if (resbody) {
|
|
96
|
-
if (resbody[entdesc.origname]) {
|
|
97
|
-
transform.resform = '`body.' + entdesc.origname + '`';
|
|
98
|
-
}
|
|
99
|
-
else if (resbody[entdesc.name]) {
|
|
100
|
-
transform.resform = '`body.' + entdesc.name + '`';
|
|
101
|
-
}
|
|
193
|
+
// TODO: capture should return these empty objects
|
|
194
|
+
caught = caught ?? {};
|
|
195
|
+
caught.methods = caught.methods ?? [];
|
|
196
|
+
caught.methods.sort((a, b) => {
|
|
197
|
+
if (a.path < b.path) {
|
|
198
|
+
return -1;
|
|
102
199
|
}
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
if (reqdef[entdesc.origname]) {
|
|
106
|
-
transform.reqform = { [entdesc.origname]: '`reqdata`' };
|
|
107
|
-
}
|
|
108
|
-
else if (reqdef[entdesc.origname]) {
|
|
109
|
-
transform.reqform = { [entdesc.origname]: '`reqdata`' };
|
|
110
|
-
}
|
|
200
|
+
else if (a.path > b.path) {
|
|
201
|
+
return 1;
|
|
111
202
|
}
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
// TODO: in actual guide, remove "standard" method ops since redundant
|
|
115
|
-
method: methodStr,
|
|
116
|
-
why_op: why_op.join(';')
|
|
117
|
-
};
|
|
118
|
-
if (0 < Object.entries(transform).length) {
|
|
119
|
-
op[opname].transform = transform;
|
|
203
|
+
else if (METHOD_CONSIDER_ORDER[a.method] < METHOD_CONSIDER_ORDER[b.method]) {
|
|
204
|
+
return -1;
|
|
120
205
|
}
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
// console.dir(entdesc, { depth: null })
|
|
124
|
-
// }
|
|
125
|
-
// })
|
|
126
|
-
// }
|
|
127
|
-
});
|
|
128
|
-
// console.log('USER')
|
|
129
|
-
// console.dir(entityDescs.user, { depth: null })
|
|
130
|
-
return entityDescs;
|
|
131
|
-
}
|
|
132
|
-
function resolveEntity(entityDescs,
|
|
133
|
-
// pathDef: Record<string, any>,
|
|
134
|
-
pathStr, methodDef, methodStr, why_ent) {
|
|
135
|
-
let entdesc;
|
|
136
|
-
let entname = '';
|
|
137
|
-
let origentname = '';
|
|
138
|
-
const why_name = [];
|
|
139
|
-
const m = pathStr.match(/\/([a-zA-Z0-1_-]+)(\/\{([a-zA-Z0-1_-]+)\})?$/);
|
|
140
|
-
if (m) {
|
|
141
|
-
let pathName = m[1];
|
|
142
|
-
let pathParam = m[3];
|
|
143
|
-
origentname = (0, jostraca_1.snakify)(pathName);
|
|
144
|
-
entname = (0, utility_1.depluralize)(origentname);
|
|
145
|
-
// Check schema
|
|
146
|
-
const compname = resolveComponentName(entname, methodDef, methodStr, pathStr, why_name);
|
|
147
|
-
if (compname) {
|
|
148
|
-
origentname = (0, jostraca_1.snakify)(compname);
|
|
149
|
-
entname = (0, utility_1.depluralize)(origentname);
|
|
150
|
-
why_ent.push('cmp:' + entname);
|
|
206
|
+
else if (METHOD_CONSIDER_ORDER[a.method] > METHOD_CONSIDER_ORDER[b.method]) {
|
|
207
|
+
return 1;
|
|
151
208
|
}
|
|
152
209
|
else {
|
|
153
|
-
|
|
154
|
-
|
|
210
|
+
return 0;
|
|
211
|
+
}
|
|
212
|
+
});
|
|
213
|
+
// console.log(caught.methods.map((n: any) => n.path + ' ' + n.method))
|
|
214
|
+
return caught.methods || [];
|
|
215
|
+
}
|
|
216
|
+
function ResolveEntityComponent(spec) {
|
|
217
|
+
const guide = spec.data.guide;
|
|
218
|
+
const metrics = guide.metrics;
|
|
219
|
+
const work = spec.data.work;
|
|
220
|
+
const methodDef = spec.node.val;
|
|
221
|
+
const methodName = methodDef.method;
|
|
222
|
+
const pathStr = methodDef.path;
|
|
223
|
+
const parts = work.pathmap[pathStr].parts;
|
|
224
|
+
let why_cmp = [];
|
|
225
|
+
let responses = methodDef.responses;
|
|
226
|
+
let origxrefs = findPotentialSchemaRefs(pathStr, methodName, responses).map(val => ({
|
|
227
|
+
val
|
|
228
|
+
}));
|
|
229
|
+
let cmpxrefs = origxrefs
|
|
230
|
+
.filter(xref => xref.val.includes('schema') || xref.val.includes('definitions'))
|
|
231
|
+
.map(xref => {
|
|
232
|
+
let m = xref.val.match(/\/components\/schemas\/(.+)$/);
|
|
233
|
+
if (!m) {
|
|
234
|
+
m = xref.val.match(/\/definitions\/(.+)$/);
|
|
235
|
+
}
|
|
236
|
+
if (m) {
|
|
237
|
+
const cmp = (0, utility_1.canonize)(m[1]);
|
|
238
|
+
xref.cmp = cmp;
|
|
239
|
+
xref.origcmp = m[1];
|
|
240
|
+
xref.origcmpref = cmp;
|
|
241
|
+
}
|
|
242
|
+
return xref;
|
|
243
|
+
})
|
|
244
|
+
.filter(xref => null != xref.cmp)
|
|
245
|
+
// TODO: identify non - ent schemas
|
|
246
|
+
.filter(xref => !xref.val.includes('Meta'));
|
|
247
|
+
let cleanxrefs = cmpxrefs
|
|
248
|
+
.map(xref => {
|
|
249
|
+
// Redundancy in cmp name, remove request,response suffix
|
|
250
|
+
// const lastPart = getelem(pathStr.split('/'), -1)
|
|
251
|
+
const lastPart = (0, struct_1.getelem)(parts, -1);
|
|
252
|
+
const lastPartLower = lastPart?.toLowerCase();
|
|
253
|
+
const lastPartCanon = (0, utility_1.canonize)(lastPart);
|
|
254
|
+
const origcmpLower = xref.origcmp?.toLowerCase();
|
|
255
|
+
if ('' !== lastPartCanon
|
|
256
|
+
&& (xref.cmp === lastPartCanon + '_response'
|
|
257
|
+
|| xref.cmp === lastPartCanon + '_request'
|
|
258
|
+
|| origcmpLower === lastPartLower + 'response'
|
|
259
|
+
|| origcmpLower === lastPartLower + 'request')) {
|
|
260
|
+
let cparts = xref.cmp.split('_');
|
|
261
|
+
// rec-canonize to deal with plural before removed suffix
|
|
262
|
+
xref.cmp = (0, utility_1.canonize)(cparts.slice(0, cparts.length - 1).join('_'));
|
|
263
|
+
}
|
|
264
|
+
return xref;
|
|
265
|
+
});
|
|
266
|
+
let goodxrefs = cleanxrefs
|
|
267
|
+
.filter(xref => {
|
|
268
|
+
if (cleanxrefs.length <= 1
|
|
269
|
+
|| pathStr.toLowerCase().includes('/' + xref.cmp + '/')
|
|
270
|
+
// || entityOccursInPath(pathStr.toLowerCase(), xref.cmp)
|
|
271
|
+
|| entityOccursInPath(parts, xref.cmp)) {
|
|
272
|
+
return true;
|
|
155
273
|
}
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
274
|
+
// Exclude high frequency suspicious cmps as probably meta data
|
|
275
|
+
const cmprefs = metrics.count.origcmprefs[xref.origcmpref] ?? 0;
|
|
276
|
+
const mcount = metrics.count.method;
|
|
277
|
+
const pcount = metrics.count.path;
|
|
278
|
+
const method_rate = (0 < mcount ? (cmprefs / mcount) : -1);
|
|
279
|
+
const path_rate = (0 < pcount ? (cmprefs / pcount) : -1);
|
|
280
|
+
// console.log('RCN', xref.cmp, cmprefs, mcount, method_rate, IS_ENTCMP_METHOD_RATE, method_rate < IS_ENTCMP_METHOD_RATE)
|
|
281
|
+
const infrequent = method_rate < IS_ENTCMP_METHOD_RATE
|
|
282
|
+
|| path_rate < IS_ENTCMP_PATH_RATE;
|
|
283
|
+
if (!infrequent) {
|
|
284
|
+
(0, utility_1.debugpath)(pathStr, methodName, 'CMP-INFREQ', xref.val, 'method:', method_rate, IS_ENTCMP_METHOD_RATE, 'path:', path_rate, IS_ENTCMP_PATH_RATE);
|
|
285
|
+
}
|
|
286
|
+
return infrequent;
|
|
287
|
+
});
|
|
288
|
+
// .sort((a, b) => a.path.length - b.path.length)
|
|
289
|
+
const fcmp = goodxrefs[0];
|
|
290
|
+
let out = undefined;
|
|
291
|
+
if (null != fcmp) {
|
|
292
|
+
out = makeMethodEntityDesc({
|
|
293
|
+
ref: fcmp.val,
|
|
294
|
+
cmp: fcmp.cmp,
|
|
295
|
+
origcmp: fcmp.origcmp,
|
|
296
|
+
origcmpref: fcmp.origcmpref,
|
|
297
|
+
entname: fcmp.cmp,
|
|
160
298
|
});
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
299
|
+
}
|
|
300
|
+
const tags = methodDef.tags ?? [];
|
|
301
|
+
const goodtags = tags.filter((tag) => {
|
|
302
|
+
const tagdesc = metrics.found.tag[tag];
|
|
303
|
+
const ctag = tagdesc?.canon;
|
|
304
|
+
return (!!metrics.found.cmp[ctag] // tag matches a cmp
|
|
305
|
+
|| null == fcmp // there's no cmp, so use tag
|
|
306
|
+
);
|
|
307
|
+
});
|
|
308
|
+
(0, utility_1.debugpath)(pathStr, methodName, 'TAGS', tags, goodtags, fcmp, methodDef, metrics.found);
|
|
309
|
+
const ftag = goodtags[0];
|
|
310
|
+
if (null != ftag) {
|
|
311
|
+
const tagdesc = metrics.found.tag[ftag];
|
|
312
|
+
const tagcmp = metrics.found.cmp[tagdesc.canon];
|
|
313
|
+
if (tagdesc && (tagcmp || null == fcmp)) {
|
|
314
|
+
if (null == out) {
|
|
315
|
+
out = makeMethodEntityDesc({
|
|
316
|
+
ref: 'tag',
|
|
317
|
+
cmp: tagdesc.canon,
|
|
318
|
+
origcmp: ftag,
|
|
319
|
+
why_cmp,
|
|
320
|
+
entname: tagdesc.canon,
|
|
321
|
+
});
|
|
322
|
+
why_cmp.push('tag=' + out.cmp);
|
|
323
|
+
}
|
|
324
|
+
else if ((pathStr.includes('/' + ftag + '/') || pathStr.includes('/' + tagdesc.canon + '/'))
|
|
325
|
+
&& out.cmp !== tagdesc.canon) {
|
|
326
|
+
out = makeMethodEntityDesc({
|
|
327
|
+
ref: 'tag',
|
|
328
|
+
cmp: tagdesc.canon,
|
|
329
|
+
origcmp: ftag,
|
|
330
|
+
why_cmp,
|
|
331
|
+
entname: tagdesc.canon,
|
|
332
|
+
});
|
|
333
|
+
why_cmp.push('tag/path=' + out.cmp);
|
|
166
334
|
}
|
|
167
335
|
}
|
|
168
336
|
}
|
|
169
|
-
|
|
337
|
+
if (null != out) {
|
|
338
|
+
why_cmp.push('cmp/resolve=' + out.cmp);
|
|
339
|
+
out.why_cmp = why_cmp;
|
|
340
|
+
out.cmpoccur = metrics.count.origcmprefs[out.origcmpref ?? ''] ?? 0;
|
|
341
|
+
out.path_rate = 0 == metrics.count.path ? -1 : (out.cmpoccur / metrics.count.path);
|
|
342
|
+
out.method_rate = 0 == metrics.count.method ? -1 : (out.cmpoccur / metrics.count.method);
|
|
343
|
+
methodDef.MethodEntity = out;
|
|
344
|
+
}
|
|
345
|
+
(0, utility_1.debugpath)(pathStr, methodName, 'CMP-NAME', out, origxrefs, cleanxrefs, goodxrefs, goodtags);
|
|
346
|
+
}
|
|
347
|
+
function ResolveEntityName(spec) {
|
|
348
|
+
const ctx = spec.ctx;
|
|
349
|
+
const data = spec.data;
|
|
350
|
+
const mdesc = spec.node.val;
|
|
351
|
+
const methodName = mdesc.method;
|
|
352
|
+
const pathStr = mdesc.path;
|
|
353
|
+
const work = spec.data.work;
|
|
354
|
+
const pathDesc = work.pathmap[pathStr];
|
|
355
|
+
const parts = pathDesc.parts;
|
|
356
|
+
work.entity.count.seen++;
|
|
357
|
+
let ment;
|
|
358
|
+
ment = mdesc.MethodEntity;
|
|
359
|
+
const why_path = [];
|
|
360
|
+
if (null == ment) {
|
|
361
|
+
why_path.push('no-desc');
|
|
362
|
+
mdesc.MethodEntity = makeMethodEntityDesc({});
|
|
363
|
+
ment = mdesc.MethodEntity;
|
|
364
|
+
}
|
|
365
|
+
why_path.push(...(ment.why_cmp ?? []));
|
|
366
|
+
let entname;
|
|
367
|
+
let pm = undefined;
|
|
368
|
+
if (pm = (0, utility_1.pathMatch)(parts, 't/p/t/')) {
|
|
369
|
+
entname = entityPathMatch_tpte(data, pm, mdesc, why_path);
|
|
370
|
+
}
|
|
371
|
+
else if (pm = (0, utility_1.pathMatch)(parts, 't/p/')) {
|
|
372
|
+
entname = entityPathMatch_tpe(data, pm, mdesc, why_path);
|
|
373
|
+
}
|
|
374
|
+
else if (pm = (0, utility_1.pathMatch)(parts, 'p/t/')) {
|
|
375
|
+
entname = entityPathMatch_pte(data, pm, mdesc, why_path);
|
|
376
|
+
}
|
|
377
|
+
else if (pm = (0, utility_1.pathMatch)(parts, 't/')) {
|
|
378
|
+
entname = entityPathMatch_te(data, pm, mdesc, why_path);
|
|
379
|
+
}
|
|
380
|
+
else if (pm = (0, utility_1.pathMatch)(parts, 't/p/p')) {
|
|
381
|
+
entname = entityPathMatch_tpp(data, pm, mdesc, why_path);
|
|
382
|
+
}
|
|
170
383
|
else {
|
|
171
|
-
|
|
172
|
-
|
|
384
|
+
work.entity.count.unresolved++;
|
|
385
|
+
entname = 'entity' + work.entity.count.unresolved;
|
|
173
386
|
}
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
387
|
+
const entdesc = work.entmap[entname] = work.entmap[entname] ?? {
|
|
388
|
+
name: entname,
|
|
389
|
+
id: 'N' + ('' + Math.random()).substring(2, 10),
|
|
390
|
+
op: {},
|
|
391
|
+
why_path,
|
|
392
|
+
...ment
|
|
393
|
+
};
|
|
178
394
|
entdesc.path = (entdesc.path || {});
|
|
179
|
-
entdesc.path[pathStr] = entdesc.path[pathStr] || {
|
|
395
|
+
entdesc.path[pathStr] = entdesc.path[pathStr] || {
|
|
396
|
+
rename: { param: {} },
|
|
397
|
+
why_rename: { why_param: {} },
|
|
398
|
+
pm,
|
|
399
|
+
};
|
|
180
400
|
entdesc.path[pathStr].op = entdesc.path[pathStr].op || {};
|
|
181
|
-
|
|
182
|
-
|
|
401
|
+
entdesc.path[pathStr].why_path = why_path;
|
|
402
|
+
ment.entname = entname;
|
|
403
|
+
ment.pm = pm;
|
|
404
|
+
(0, utility_1.debugpath)(pathStr, methodName, 'RESOLVE-ENTITY-NAME', (0, utility_1.formatJSONIC)({ entdesc, ment }, { hsepd: 0, $: true, color: true }));
|
|
405
|
+
}
|
|
406
|
+
function RenameParams(spec) {
|
|
407
|
+
const ctx = spec.ctx;
|
|
408
|
+
const data = spec.data;
|
|
409
|
+
const guide = data.guide;
|
|
410
|
+
const metrics = guide.metrics;
|
|
411
|
+
const mdesc = spec.node.val;
|
|
412
|
+
const ment = mdesc.MethodEntity;
|
|
413
|
+
const pathStr = mdesc.path;
|
|
414
|
+
const work = spec.data.work;
|
|
415
|
+
const entname = mdesc.MethodEntity.entname;
|
|
416
|
+
const entdesc = work.entmap[entname];
|
|
417
|
+
const pathdesc = spec.data.work.pathmap[pathStr];
|
|
418
|
+
const methodName = mdesc.method;
|
|
419
|
+
// Rewrite path parameters that are identifiers to follow the rules:
|
|
420
|
+
// 0. Parameters named [a-z]?id are considered identifiers
|
|
421
|
+
// 1. last identifier is always {id} as this is the primary entity
|
|
422
|
+
// 2. internal identifiers are formatted as {name_id} where name is the parent entity name
|
|
423
|
+
// Example: /api/bar/{id}/zed/{zid}/foo/{fid} ->
|
|
424
|
+
// /api/bar/{bar_id}/zed/{zed_id}/foo/{id}
|
|
425
|
+
// id needs to be t/p/
|
|
426
|
+
const multParamEndMatch = (0, utility_1.pathMatch)(mdesc.path, 'p/p/');
|
|
427
|
+
if (multParamEndMatch) {
|
|
428
|
+
return;
|
|
183
429
|
}
|
|
184
|
-
|
|
430
|
+
const pathDesc = entdesc.path[pathStr];
|
|
431
|
+
pathDesc.rename = (pathDesc.rename ?? { param: {} });
|
|
432
|
+
pathDesc.why_rename = (pathDesc.why_rename ?? { why_param: {} });
|
|
433
|
+
pathDesc.action = (pathDesc.action ?? {});
|
|
434
|
+
pathDesc.why_action = (pathDesc.why_action ?? {});
|
|
435
|
+
const paramRenameCapture = {
|
|
436
|
+
rename: pathDesc.rename.param = (pathDesc.rename.param ?? {}),
|
|
437
|
+
why: pathDesc.why_rename.why_param = (pathDesc.why_rename.why_param ?? {}),
|
|
438
|
+
};
|
|
439
|
+
const parts = pathdesc.parts;
|
|
440
|
+
const cmpname = mdesc.cmp;
|
|
441
|
+
const considerCmp = null != cmpname &&
|
|
442
|
+
0 < metrics.count.uniqschema &&
|
|
443
|
+
mdesc.method_rate < IS_ENTCMP_METHOD_RATE;
|
|
444
|
+
const origParams = [];
|
|
445
|
+
for (let partI = 0; partI < parts.length; partI++) {
|
|
446
|
+
let partStr = parts[partI];
|
|
447
|
+
if (isParam(partStr)) {
|
|
448
|
+
origParams.push(partStr.replace(/[\}\{\*]/g, ''));
|
|
449
|
+
const why = [];
|
|
450
|
+
const oldParam = partStr.substring(1, partStr.length - 1);
|
|
451
|
+
paramRenameCapture.why[oldParam] = (paramRenameCapture.why[oldParam] ?? []);
|
|
452
|
+
const lastPart = partI === parts.length - 1;
|
|
453
|
+
const secondLastPart = partI === parts.length - 2;
|
|
454
|
+
const notLastPart = partI < parts.length - 1;
|
|
455
|
+
const hasParent = 0 < partI && !isParam(parts[partI - 1]);
|
|
456
|
+
const parentName = hasParent ? (0, utility_1.canonize)(parts[partI - 1]) : null;
|
|
457
|
+
const not_exact_id = 'id' !== oldParam;
|
|
458
|
+
const probably_an_id = oldParam.endsWith('id')
|
|
459
|
+
|| oldParam.endsWith('Id')
|
|
460
|
+
|| (0, utility_1.canonize)(oldParam) === parentName;
|
|
461
|
+
(0, utility_1.debugpath)(pathStr, mdesc.method, 'RENAME-PARAM-PART', parts, partI, partStr, {
|
|
462
|
+
lastPart,
|
|
463
|
+
secondLastPart,
|
|
464
|
+
notLastPart,
|
|
465
|
+
hasParent,
|
|
466
|
+
parentName,
|
|
467
|
+
not_exact_id,
|
|
468
|
+
probably_an_id,
|
|
469
|
+
});
|
|
470
|
+
// Id-like not at end, and after a possible entname.
|
|
471
|
+
// .../parentent/{id}/...
|
|
472
|
+
if (probably_an_id
|
|
473
|
+
&& hasParent
|
|
474
|
+
&& notLastPart) {
|
|
475
|
+
why.push('maybe-parent');
|
|
476
|
+
// actually an action
|
|
477
|
+
if (secondLastPart
|
|
478
|
+
&& ((parentName !== entdesc.name
|
|
479
|
+
&& entdesc.name.startsWith(parentName + '_'))
|
|
480
|
+
// || parentName === cmp.name
|
|
481
|
+
|| parentName === cmpname)) {
|
|
482
|
+
// let newParamName = 'id'
|
|
483
|
+
updateParamRename(ctx, data, pathStr, methodName, paramRenameCapture, oldParam, 'id', 'action-parent:' + entdesc.name);
|
|
484
|
+
why.push('action');
|
|
485
|
+
updateAction(methodName, oldParam, parts[partI + 1], entdesc, pathDesc, 'action-not-parent');
|
|
486
|
+
}
|
|
487
|
+
else if (hasParent && parentName === cmpname) {
|
|
488
|
+
updateParamRename(ctx, data, pathStr, methodName, paramRenameCapture, oldParam, 'id', 'id-parent-cmp');
|
|
489
|
+
why.push('id-parent-cmp');
|
|
490
|
+
}
|
|
491
|
+
else if (hasParent && parentName === entdesc.name) {
|
|
492
|
+
updateParamRename(ctx, data, pathStr, methodName, paramRenameCapture, oldParam, 'id', 'id-parent-ent');
|
|
493
|
+
why.push('id-parent-ent');
|
|
494
|
+
}
|
|
495
|
+
else {
|
|
496
|
+
updateParamRename(ctx, data, pathStr, methodName, paramRenameCapture, oldParam, parentName + '_id', 'parent:' + parentName);
|
|
497
|
+
why.push('parent');
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
// /api/foo/{foo}/bar/...
|
|
501
|
+
// param matches parent entname, but is not _id format
|
|
502
|
+
// At end, but not called id.
|
|
503
|
+
// .../ent/{not-id}
|
|
504
|
+
else if (lastPart
|
|
505
|
+
&& not_exact_id
|
|
506
|
+
&& (!hasParent
|
|
507
|
+
|| (parentName === entdesc.name
|
|
508
|
+
|| entdesc.name.endsWith('_' + parentName)))
|
|
509
|
+
&& (!considerCmp || cmpname === entdesc.name)) {
|
|
510
|
+
updateParamRename(ctx, data, pathStr, methodName, paramRenameCapture, oldParam, 'id', 'end-id;' + methodName + ';parent=' + hasParent + '/' + parentName +
|
|
511
|
+
';cmp=' + considerCmp + (null == cmpname ? '' : '/' + cmpname));
|
|
512
|
+
why.push('end-id');
|
|
513
|
+
}
|
|
514
|
+
// Mot at end, has preceding non-param part.
|
|
515
|
+
// .../parentent/{paramname}/...
|
|
516
|
+
else if (notLastPart
|
|
517
|
+
&& 1 < partI
|
|
518
|
+
&& hasParent) {
|
|
519
|
+
why.push('has-parent');
|
|
520
|
+
// Actually primary ent with an action$ suffix
|
|
521
|
+
if (secondLastPart) {
|
|
522
|
+
why.push('second-last');
|
|
523
|
+
if ('id' !== oldParam
|
|
524
|
+
// && fixEntName(partStr) === entdesc.name
|
|
525
|
+
&& (0, utility_1.canonize)(partStr) === entdesc.name) {
|
|
526
|
+
updateParamRename(ctx, data, pathStr, methodName, paramRenameCapture, oldParam, 'id', 'end-action');
|
|
527
|
+
why.push('end-action');
|
|
528
|
+
updateAction(methodName, oldParam, parts[partI + 1], entdesc, pathDesc, 'end-action');
|
|
529
|
+
}
|
|
530
|
+
else {
|
|
531
|
+
why.push('not-end-action');
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
// Primary ent id not at end!
|
|
535
|
+
else if (hasParent
|
|
536
|
+
&& parentName === cmpname) {
|
|
537
|
+
updateParamRename(ctx, data, pathStr, methodName, paramRenameCapture, oldParam, 'id', 'id-not-last');
|
|
538
|
+
why.push('id-not-last');
|
|
539
|
+
// paramRenames[oldParam] = 'id'
|
|
540
|
+
// paramRenamesWhy[oldParam].push('id-not-last')
|
|
541
|
+
}
|
|
542
|
+
// Not primary ent.
|
|
543
|
+
else {
|
|
544
|
+
why.push('default');
|
|
545
|
+
let newParamName = parentName + '_id';
|
|
546
|
+
if (newParamName != oldParam) {
|
|
547
|
+
updateParamRename(ctx, data, pathStr, methodName, paramRenameCapture, oldParam, newParamName, 'not-primary');
|
|
548
|
+
why.push('not-primary');
|
|
549
|
+
// paramRenames[oldParam] = newParamName
|
|
550
|
+
// paramRenamesWhy[oldParam].push('not-primary')
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
why.push('done');
|
|
555
|
+
if (paramRenameCapture.rename[oldParam] === oldParam) {
|
|
556
|
+
why.push('delete-dup');
|
|
557
|
+
delete paramRenameCapture.rename[oldParam];
|
|
558
|
+
delete paramRenameCapture.why[oldParam];
|
|
559
|
+
}
|
|
560
|
+
// TODO: these need to done via an API
|
|
561
|
+
(0, utility_1.debugpath)(pathStr, methodName, 'RENAME-PARAM', {
|
|
562
|
+
pathStr,
|
|
563
|
+
methodName,
|
|
564
|
+
partStr,
|
|
565
|
+
why,
|
|
566
|
+
oldParam,
|
|
567
|
+
lastPart,
|
|
568
|
+
secondLastPart,
|
|
569
|
+
notLastPart,
|
|
570
|
+
hasParent,
|
|
571
|
+
parentName,
|
|
572
|
+
not_exact_id,
|
|
573
|
+
probably_an_id,
|
|
574
|
+
considerCmp,
|
|
575
|
+
cmp: mdesc.cmp,
|
|
576
|
+
cmpname,
|
|
577
|
+
paramRenameCapture,
|
|
578
|
+
entdesc
|
|
579
|
+
});
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
ment.rename = paramRenameCapture.rename;
|
|
583
|
+
ment.why_rename = paramRenameCapture.why;
|
|
584
|
+
ment.rename_orig = origParams;
|
|
185
585
|
}
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
586
|
+
function FindActions(spec) {
|
|
587
|
+
const mdesc = spec.node.val;
|
|
588
|
+
const pathStr = mdesc.path;
|
|
589
|
+
const work = spec.data.work;
|
|
590
|
+
const ment = mdesc.MethodEntity;
|
|
591
|
+
const entname = ment.entname;
|
|
592
|
+
const entdesc = work.entmap[entname];
|
|
593
|
+
// const pathdesc = spec.data.work.pathmap[pathStr]
|
|
594
|
+
const pathdesc = entdesc.path[pathStr];
|
|
595
|
+
const methodName = mdesc.method;
|
|
596
|
+
pathdesc.action = (pathdesc.action ?? {});
|
|
597
|
+
pathdesc.why_action = (pathdesc.why_action ?? {});
|
|
598
|
+
const parts = spec.data.work.pathmap[pathStr].parts;
|
|
599
|
+
const fourthLastPart = parts[parts.length - 4];
|
|
600
|
+
const fourthLastPartCanon = (0, utility_1.canonize)(fourthLastPart);
|
|
601
|
+
const thirdLastPart = parts[parts.length - 3];
|
|
602
|
+
const thirdLastPartCanon = (0, utility_1.canonize)(thirdLastPart);
|
|
603
|
+
const secondLastPart = parts[parts.length - 2];
|
|
604
|
+
const secondLastPartCanon = (0, utility_1.canonize)(secondLastPart);
|
|
605
|
+
const lastPart = parts[parts.length - 1];
|
|
606
|
+
const lastPartCanon = (0, utility_1.canonize)(lastPart);
|
|
607
|
+
const cmp = ment.cmp;
|
|
608
|
+
// /api/foo/bar where foo is the entity and bar is the action, no id param
|
|
609
|
+
if (secondLastPartCanon === cmp
|
|
610
|
+
|| secondLastPartCanon === ment.origcmp
|
|
611
|
+
|| secondLastPartCanon === entname) {
|
|
612
|
+
if (!isParam(lastPart)) {
|
|
613
|
+
updateAction(methodName, lastPart, lastPartCanon, entdesc, pathdesc, 'no-param');
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
// /api/foo/{param}/action
|
|
617
|
+
else if (thirdLastPartCanon === cmp
|
|
618
|
+
|| thirdLastPartCanon === ment.origcmp
|
|
619
|
+
|| thirdLastPartCanon === entname) {
|
|
620
|
+
if (isParam(secondLastPart) && !isParam(lastPart)) {
|
|
621
|
+
updateAction(methodName, lastPart, lastPartCanon, entdesc, pathdesc, 'ent-param-2nd-last');
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
// /api/foo/{param}/action/subaction
|
|
625
|
+
else if (fourthLastPartCanon === cmp
|
|
626
|
+
|| fourthLastPartCanon === ment.origcmp
|
|
627
|
+
|| fourthLastPartCanon === entname) {
|
|
628
|
+
if (isParam(thirdLastPart) && !isParam(secondLastPart) && !isParam(lastPart)) {
|
|
629
|
+
const oldActionName = secondLastPart + '/' + lastPart;
|
|
630
|
+
const actionName = secondLastPartCanon + '_' + lastPartCanon;
|
|
631
|
+
updateAction(methodName, oldActionName, actionName, entdesc, pathdesc, 'ent-param-3rd-last');
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
(0, utility_1.debugpath)(pathStr, methodName, 'FIND-ACTIONS', cmp, parts, pathdesc.action, pathdesc.why_action);
|
|
635
|
+
// return pathdesc.action
|
|
636
|
+
}
|
|
637
|
+
function ResolveOperation(spec) {
|
|
638
|
+
const mdesc = spec.node.val;
|
|
639
|
+
const ment = mdesc.MethodEntity;
|
|
640
|
+
const pathStr = mdesc.path;
|
|
641
|
+
const work = spec.data.work;
|
|
642
|
+
const parts = work.pathmap[pathStr].parts;
|
|
643
|
+
const entname = mdesc.MethodEntity.entname;
|
|
644
|
+
const entdesc = work.entmap[entname];
|
|
645
|
+
const methodName = mdesc.method;
|
|
646
|
+
const why_op = ment.why_op = [];
|
|
647
|
+
let opname = METHOD_IDOP[methodName];
|
|
648
|
+
let standard_opname = opname;
|
|
220
649
|
if (null == opname) {
|
|
221
|
-
|
|
650
|
+
why_op.push('no-op:' + methodName);
|
|
222
651
|
return;
|
|
223
652
|
}
|
|
224
|
-
|
|
225
|
-
|
|
653
|
+
// REVIEW: using POST and PUT in non-restian ways is too wierd to handle consistently
|
|
654
|
+
// correct using guide customizations
|
|
655
|
+
// Sometimes POST is used to update, not create. Attempt to identify this.
|
|
656
|
+
// And sometimes vice versa for PUT
|
|
657
|
+
// const id_param_offset = ment.pm?.expr?.endsWith('/t/') ? 1 : 0
|
|
658
|
+
// const has_end_id_param =
|
|
659
|
+
// entname == canonize(parts[parts.length - 2 - id_param_offset])
|
|
660
|
+
// && parts[parts.length - 1 - id_param_offset]?.toLowerCase().endsWith('id}')
|
|
661
|
+
if ('load' === standard_opname) {
|
|
662
|
+
const islist = isListResponse(mdesc, pathStr, why_op);
|
|
226
663
|
opname = islist ? 'list' : opname;
|
|
227
|
-
// console.log('ISLIST', entdesc.name, methodStr, opname, pathStr)
|
|
228
664
|
}
|
|
665
|
+
/*
|
|
666
|
+
else if (
|
|
667
|
+
'create' === standard_opname
|
|
668
|
+
&& has_end_id_param
|
|
669
|
+
) {
|
|
670
|
+
opname = 'update'
|
|
671
|
+
why_op.push('id-present')
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
else if (
|
|
675
|
+
'update' === standard_opname
|
|
676
|
+
&& !has_end_id_param
|
|
677
|
+
) {
|
|
678
|
+
opname = 'create'
|
|
679
|
+
why_op.push('no-id-present')
|
|
680
|
+
}
|
|
681
|
+
*/
|
|
229
682
|
else {
|
|
230
|
-
|
|
683
|
+
why_op.push('not-load');
|
|
684
|
+
}
|
|
685
|
+
// why.push('ent=' + entdesc.name)
|
|
686
|
+
ment.opname = opname;
|
|
687
|
+
ment.why_opname = why_op;
|
|
688
|
+
const op = entdesc.path[pathStr].op;
|
|
689
|
+
const opdef = {
|
|
690
|
+
method: methodName,
|
|
691
|
+
why_op: why_op.join(';')
|
|
692
|
+
};
|
|
693
|
+
if (null == op[opname]) {
|
|
694
|
+
op[opname] = opdef;
|
|
231
695
|
}
|
|
232
|
-
|
|
696
|
+
// Conflicting methods for same operation
|
|
697
|
+
// METHOD_CONSIDER_ORDER wins
|
|
698
|
+
// Add operation using method name
|
|
699
|
+
else {
|
|
700
|
+
op[methodName.toLowerCase()] = opdef;
|
|
701
|
+
}
|
|
702
|
+
(0, utility_1.debugpath)(pathStr, methodName, 'ResolveOperation', standard_opname, opname, why_op, op);
|
|
233
703
|
}
|
|
234
|
-
function
|
|
235
|
-
const
|
|
236
|
-
|
|
237
|
-
|
|
704
|
+
function ResolveTransform(spec) {
|
|
705
|
+
const mdesc = spec.node.val;
|
|
706
|
+
const ment = mdesc.MethodEntity;
|
|
707
|
+
const pathStr = mdesc.path;
|
|
708
|
+
const work = spec.data.work;
|
|
709
|
+
const entname = mdesc.MethodEntity.entname;
|
|
710
|
+
const entdesc = work.entmap[entname];
|
|
711
|
+
// const pathdesc = spec.data.work.pathmap[pathStr]
|
|
712
|
+
const pathdesc = entdesc.path[pathStr];
|
|
713
|
+
const methodName = mdesc.method;
|
|
714
|
+
const opname = ment.opname;
|
|
715
|
+
const op = pathdesc.op;
|
|
716
|
+
const transform = {
|
|
717
|
+
req: undefined,
|
|
718
|
+
res: undefined,
|
|
719
|
+
};
|
|
720
|
+
const resokdef = mdesc.responses?.[200] || mdesc.responses?.[201];
|
|
721
|
+
const resprops = getResponseSchema(resokdef)?.properties;
|
|
722
|
+
(0, utility_1.debugpath)(pathStr, methodName, 'TRANSFORM-RES', (0, struct_1.keysof)(resprops));
|
|
723
|
+
if (resprops) {
|
|
724
|
+
if (resprops[entdesc.origname]) {
|
|
725
|
+
transform.res = '`body.' + entdesc.origname + '`';
|
|
726
|
+
}
|
|
727
|
+
else if (resprops[entdesc.name]) {
|
|
728
|
+
transform.res = '`body.' + entdesc.name + '`';
|
|
238
729
|
}
|
|
730
|
+
}
|
|
731
|
+
const reqprops = getRequestBodySchema(mdesc.requestBody);
|
|
732
|
+
(0, utility_1.debugpath)(pathStr, methodName, 'TRANSFORM-REQ', (0, struct_1.keysof)(reqprops));
|
|
733
|
+
if (reqprops) {
|
|
734
|
+
if (reqprops[entdesc.origname]) {
|
|
735
|
+
transform.req = { [entdesc.origname]: '`reqdata`' };
|
|
736
|
+
}
|
|
737
|
+
else if (reqprops[entdesc.origname]) {
|
|
738
|
+
transform.req = { [entdesc.origname]: '`reqdata`' };
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
if (!(0, struct_1.isempty)(transform)) {
|
|
742
|
+
op[opname].transform = transform;
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
function BuildEntity(spec) {
|
|
746
|
+
const entdesc = spec.node.val;
|
|
747
|
+
// console.log('BUILD-ENTITY')
|
|
748
|
+
// console.dir(entdesc, { depth: null })
|
|
749
|
+
const guide = spec.data.guide;
|
|
750
|
+
guide.metrics.count.entity++;
|
|
751
|
+
const entityMap = guide.entity;
|
|
752
|
+
const path = {};
|
|
753
|
+
const rename_param = (pathdesc) => {
|
|
754
|
+
// console.log('RENAME-PATHDESC', pathdesc)
|
|
755
|
+
const out = {};
|
|
756
|
+
(0, jostraca_1.each)(pathdesc.rename.param, (item) => {
|
|
757
|
+
out[item.key$] = {
|
|
758
|
+
target: item.val$,
|
|
759
|
+
why_rename: pathdesc.why_rename.why_param[item.key$]
|
|
760
|
+
};
|
|
761
|
+
});
|
|
762
|
+
return out;
|
|
763
|
+
};
|
|
764
|
+
(0, jostraca_1.each)(entdesc.path, (pathdesc, pathstr) => {
|
|
765
|
+
const guidepath = {
|
|
766
|
+
why_path: pathdesc.why_path,
|
|
767
|
+
action: pathdesc.action,
|
|
768
|
+
rename: {
|
|
769
|
+
param: rename_param(pathdesc)
|
|
770
|
+
},
|
|
771
|
+
op: pathdesc.op
|
|
772
|
+
};
|
|
773
|
+
path[pathstr] = guidepath;
|
|
239
774
|
});
|
|
240
|
-
|
|
775
|
+
entityMap[entdesc.name] = {
|
|
776
|
+
name: entdesc.name,
|
|
777
|
+
orig: entdesc.origcmp,
|
|
778
|
+
path,
|
|
779
|
+
};
|
|
780
|
+
}
|
|
781
|
+
function entityPathMatch_tpte(data, pm, mdesc, why) {
|
|
782
|
+
const ment = mdesc.MethodEntity;
|
|
783
|
+
const pathNameIndex = 2;
|
|
784
|
+
why.push('path=t/p/t/');
|
|
785
|
+
const origPathName = pm[pathNameIndex];
|
|
786
|
+
let entname = (0, utility_1.canonize)(origPathName);
|
|
787
|
+
let ecm = undefined;
|
|
788
|
+
if (null != ment.cmp) {
|
|
789
|
+
ecm = entityCmpMatch(data, entname, mdesc, why);
|
|
790
|
+
entname = ecm.name;
|
|
791
|
+
why.push('has-cmp=' + ecm.orig);
|
|
792
|
+
}
|
|
793
|
+
else if (probableEntityMethod(data, ment, pm, why)) {
|
|
794
|
+
ecm = entityCmpMatch(data, entname, mdesc, why);
|
|
795
|
+
if (ecm.cmpish) {
|
|
796
|
+
entname = ecm.name;
|
|
797
|
+
why.push('prob-ent=' + ecm.orig);
|
|
798
|
+
}
|
|
799
|
+
else if (endsWithCmp(data, pm)) {
|
|
800
|
+
entname = (0, utility_1.canonize)((0, struct_1.getelem)(pm, -1));
|
|
801
|
+
why.push('prob-ent-last=' + ecm.orig);
|
|
802
|
+
}
|
|
803
|
+
else if (0 < (0, utility_1.findPathsWithPrefix)(data, pm.path, { strict: true })) {
|
|
804
|
+
entname = (0, utility_1.canonize)((0, struct_1.getelem)(pm, -1));
|
|
805
|
+
why.push('prob-ent-prefix=' + ecm.orig);
|
|
806
|
+
}
|
|
807
|
+
else {
|
|
808
|
+
entname = (0, utility_1.canonize)((0, struct_1.getelem)(pm, -3)) + '_' + entname;
|
|
809
|
+
why.push('prob-ent-part');
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
// Probably an entity action suffix
|
|
813
|
+
else {
|
|
814
|
+
why.push('prob-ent-act');
|
|
815
|
+
entname = (0, utility_1.canonize)((0, struct_1.getelem)(pm, -3));
|
|
816
|
+
}
|
|
817
|
+
return entname;
|
|
818
|
+
}
|
|
819
|
+
function endsWithCmp(data, pm) {
|
|
820
|
+
const last = (0, utility_1.canonize)((0, struct_1.getelem)(pm, -1));
|
|
821
|
+
return isOrigCmp(data, last);
|
|
822
|
+
}
|
|
823
|
+
function isOrigCmp(data, name) {
|
|
824
|
+
return null != data.metrics.count.origcmprefs[name];
|
|
825
|
+
}
|
|
826
|
+
function entityOccursInPath(parts, entname) {
|
|
827
|
+
let partsLower = parts.map(p => p.toLowerCase());
|
|
828
|
+
partsLower = partsLower.filter(p => '{' !== p[0]).map(p => (0, utility_1.canonize)(p));
|
|
829
|
+
return !partsLower.reduce((a, p) => (a && p !== entname), true);
|
|
830
|
+
}
|
|
831
|
+
function entityPathMatch_tpe(data, pm, mdesc, why) {
|
|
832
|
+
const ment = mdesc.MethodEntity;
|
|
833
|
+
const pathNameIndex = 0;
|
|
834
|
+
why.push('path=t/p/');
|
|
835
|
+
const origPathName = pm[pathNameIndex];
|
|
836
|
+
// let entname = fixEntName(origPathName)
|
|
837
|
+
let entname = (0, utility_1.canonize)(origPathName);
|
|
838
|
+
if (null != ment.cmp || probableEntityMethod(data, mdesc, pm, why)) {
|
|
839
|
+
let ecm = entityCmpMatch(data, entname, mdesc, why);
|
|
840
|
+
entname = ecm.name;
|
|
841
|
+
}
|
|
842
|
+
else {
|
|
843
|
+
why.push('ent-act');
|
|
844
|
+
}
|
|
845
|
+
return entname;
|
|
846
|
+
}
|
|
847
|
+
function entityPathMatch_pte(data, pm, mdesc, why) {
|
|
848
|
+
const ment = mdesc.MethodEntity;
|
|
849
|
+
const pathNameIndex = 1;
|
|
850
|
+
why.push('path=p/t/');
|
|
851
|
+
const origPathName = pm[pathNameIndex];
|
|
852
|
+
let entname = (0, utility_1.canonize)(origPathName);
|
|
853
|
+
if (null != ment.cmp || probableEntityMethod(data, mdesc, pm, why)) {
|
|
854
|
+
let ecm = entityCmpMatch(data, entname, mdesc, why);
|
|
855
|
+
entname = ecm.name;
|
|
856
|
+
}
|
|
857
|
+
else {
|
|
858
|
+
why.push('ent-act');
|
|
859
|
+
}
|
|
860
|
+
return entname;
|
|
861
|
+
}
|
|
862
|
+
function entityPathMatch_te(data, pm, mdesc, why) {
|
|
863
|
+
const ment = mdesc.MethodEntity;
|
|
864
|
+
const pathNameIndex = 0;
|
|
865
|
+
why.push('path=t/');
|
|
866
|
+
const origPathName = pm[pathNameIndex];
|
|
867
|
+
// let entname = fixEntName(origPathName)
|
|
868
|
+
let entname = (0, utility_1.canonize)(origPathName);
|
|
869
|
+
if (null != ment.cmp || probableEntityMethod(data, mdesc, pm, why)) {
|
|
870
|
+
let ecm = entityCmpMatch(data, entname, mdesc, why);
|
|
871
|
+
entname = ecm.name;
|
|
872
|
+
}
|
|
873
|
+
else {
|
|
874
|
+
why.push('ent-act');
|
|
875
|
+
}
|
|
876
|
+
return entname;
|
|
877
|
+
}
|
|
878
|
+
function entityPathMatch_tpp(data, pm, mdesc, why) {
|
|
879
|
+
const ment = mdesc.MethodEntity;
|
|
880
|
+
const pathNameIndex = 0;
|
|
881
|
+
why.push('path=t/p/p');
|
|
882
|
+
const origPathName = pm[pathNameIndex];
|
|
883
|
+
// let entname = fixEntName(origPathName)
|
|
884
|
+
let entname = (0, utility_1.canonize)(origPathName);
|
|
885
|
+
if (null != ment.cmp || probableEntityMethod(data, mdesc, pm, why)) {
|
|
886
|
+
let ecm = entityCmpMatch(data, entname, mdesc, why);
|
|
887
|
+
entname = ecm.name;
|
|
888
|
+
}
|
|
889
|
+
else {
|
|
890
|
+
why.push('ent-act');
|
|
891
|
+
}
|
|
892
|
+
return entname;
|
|
893
|
+
}
|
|
894
|
+
function getRequestBodySchema(requestBody) {
|
|
895
|
+
return requestBody?.content?.['application/json']?.schema ??
|
|
896
|
+
requestBody?.schema;
|
|
897
|
+
}
|
|
898
|
+
function getResponseSchema(response) {
|
|
899
|
+
return response?.content?.['application/json']?.schema ??
|
|
900
|
+
response?.schema;
|
|
901
|
+
}
|
|
902
|
+
// No entity component was found, but there still might be an entity.
|
|
903
|
+
function probableEntityMethod(data, mdesc, pm, why) {
|
|
904
|
+
const request = mdesc.requestBody;
|
|
905
|
+
const reqSchema = getRequestBodySchema(request);
|
|
906
|
+
const response = mdesc.responses?.['201'] || mdesc.responses?.['200'];
|
|
907
|
+
const resSchema = getResponseSchema(response);
|
|
908
|
+
const noResponse = null == resSchema && null != mdesc.responses?.['204'];
|
|
909
|
+
let prob_why = '';
|
|
910
|
+
let probent = false;
|
|
911
|
+
if (noResponse) {
|
|
912
|
+
// No response at all means not an action, thus probably an entity.
|
|
913
|
+
prob_why = 'nores';
|
|
914
|
+
probent = true;
|
|
915
|
+
}
|
|
916
|
+
else if (null != reqSchema) {
|
|
917
|
+
if ('POST' === mdesc.method
|
|
918
|
+
&& !pm.expr.endsWith('/p/')
|
|
919
|
+
// A real entity would probably occur in at least one other t/p path
|
|
920
|
+
// otherwise this is probably an action
|
|
921
|
+
&& (1 < Object.keys(data.def.paths).filter(path => path.includes('/' + pm[pm.length - 1] + '/')).length)) {
|
|
922
|
+
prob_why = 'post';
|
|
923
|
+
probent = true;
|
|
924
|
+
}
|
|
925
|
+
else if (('PUT' === mdesc.method || 'PATCH' === mdesc.method)
|
|
926
|
+
&& pm.expr.endsWith('/p/')) {
|
|
927
|
+
prob_why = 'putish';
|
|
928
|
+
probent = true;
|
|
929
|
+
}
|
|
930
|
+
}
|
|
931
|
+
else if ('GET' === mdesc.method) {
|
|
932
|
+
prob_why = 'get';
|
|
933
|
+
probent = true;
|
|
934
|
+
}
|
|
935
|
+
const rescodes = Object.keys(mdesc.responses ?? {});
|
|
936
|
+
(0, utility_1.debugpath)(mdesc.path, mdesc.method, 'PROBABLE-ENTITY-RESPONSE', { mdesc, responses: rescodes, probent, prob_why });
|
|
937
|
+
why.push('entres=' + probent + '/' + rescodes + ('' === prob_why ? '' : '/' + prob_why));
|
|
938
|
+
return probent;
|
|
939
|
+
}
|
|
940
|
+
function entityCmpMatch(data, entname, mdesc, why) {
|
|
941
|
+
const ment = mdesc.MethodEntity;
|
|
942
|
+
let out = {
|
|
943
|
+
name: entname,
|
|
944
|
+
orig: ment.origcmp ?? entname,
|
|
945
|
+
cmpish: false,
|
|
946
|
+
pathish: true,
|
|
947
|
+
};
|
|
948
|
+
// console.log('ECM-A', out, ment)
|
|
949
|
+
const cmpInfrequent = (ment.method_rate < IS_ENTCMP_METHOD_RATE
|
|
950
|
+
|| ment.path_rate < IS_ENTCMP_PATH_RATE);
|
|
951
|
+
if (null != ment.cmp
|
|
952
|
+
&& entname != ment.cmp
|
|
953
|
+
&& !ment.cmp.startsWith(entname)) {
|
|
954
|
+
if (cmpInfrequent) {
|
|
955
|
+
why.push('cmp-primary');
|
|
956
|
+
out.name = ment.cmp;
|
|
957
|
+
out.orig = ment.origcmp;
|
|
958
|
+
out.cmpish = true;
|
|
959
|
+
out.pathish = false;
|
|
960
|
+
why.push('cmp-infreq');
|
|
961
|
+
}
|
|
962
|
+
else if (cmpOccursInPath(data, ment.cmp)) {
|
|
963
|
+
why.push('cmp-path');
|
|
964
|
+
out.name = ment.cmp;
|
|
965
|
+
out.orig = ment.origcmp;
|
|
966
|
+
out.cmpish = true;
|
|
967
|
+
out.pathish = false;
|
|
968
|
+
why.push('cmp-inpath');
|
|
969
|
+
}
|
|
970
|
+
else {
|
|
971
|
+
why.push('path-over-cmp');
|
|
972
|
+
}
|
|
973
|
+
}
|
|
974
|
+
else if ('DELETE' === mdesc.method
|
|
975
|
+
&& null == ment.cmp) {
|
|
976
|
+
let cmps = findcmps(data, mdesc.path, ['responses'], { uniq: true });
|
|
977
|
+
if (1 === cmps.length) {
|
|
978
|
+
out.name = cmps[0].cmp;
|
|
979
|
+
out.orig = cmps[0].origcmp;
|
|
980
|
+
out.cmpish = true;
|
|
981
|
+
out.pathish = false;
|
|
982
|
+
why.push('cmp-found-delete');
|
|
983
|
+
}
|
|
984
|
+
else {
|
|
985
|
+
why.push('path-primary-delete');
|
|
986
|
+
}
|
|
987
|
+
}
|
|
988
|
+
else {
|
|
989
|
+
why.push('path-primary');
|
|
990
|
+
}
|
|
991
|
+
(0, utility_1.debugpath)(mdesc.path, mdesc.method, 'ENTITY-CMP-NAME', mdesc.path, mdesc.method, entname + '->', out, why, ment, IS_ENTCMP_METHOD_RATE, IS_ENTCMP_PATH_RATE);
|
|
992
|
+
// console.log('ECM-Z', out, why, ment)
|
|
993
|
+
return out;
|
|
994
|
+
}
|
|
995
|
+
function cmpOccursInPath(data, cmpname) {
|
|
996
|
+
if (null == data.work.potentialCmpsFromPaths) {
|
|
997
|
+
data.work.potentialCmpsFromPaths = {};
|
|
998
|
+
(0, jostraca_1.each)(data.def.paths, (_pathdef, pathstr) => {
|
|
999
|
+
const parts = data.work.pathmap[pathstr].parts;
|
|
1000
|
+
parts
|
|
1001
|
+
.filter(p => !p.startsWith('{'))
|
|
1002
|
+
.map(p => data.work.potentialCmpsFromPaths[(0, utility_1.canonize)(p)] = true);
|
|
1003
|
+
});
|
|
1004
|
+
}
|
|
1005
|
+
return null != data.work.potentialCmpsFromPaths[cmpname];
|
|
1006
|
+
}
|
|
1007
|
+
function isListResponse(mdesc, pathStr, why) {
|
|
1008
|
+
const ment = mdesc.MethodEntity;
|
|
1009
|
+
const pm = ment.pm;
|
|
241
1010
|
let islist = false;
|
|
242
|
-
|
|
243
|
-
|
|
1011
|
+
let schema;
|
|
1012
|
+
if (pm && pm.expr.endsWith('p/')) {
|
|
1013
|
+
why.push('end-param');
|
|
244
1014
|
}
|
|
245
1015
|
else {
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
if (
|
|
274
|
-
why.push('
|
|
1016
|
+
let caught = (0, utility_1.capture)(mdesc, {
|
|
1017
|
+
responses:
|
|
1018
|
+
// '`$ANY`': { content: { 'application/json': { schema: '`$CAPTURE`' } } },
|
|
1019
|
+
['`$SELECT`', { '$KEY': { '`$OR`': ['200', '201'] } },
|
|
1020
|
+
{ content: { 'application/json': { schema: '`$CAPTURE`' } } }],
|
|
1021
|
+
});
|
|
1022
|
+
schema = caught.schema;
|
|
1023
|
+
if (null == schema) {
|
|
1024
|
+
caught = (0, utility_1.capture)(mdesc, {
|
|
1025
|
+
responses:
|
|
1026
|
+
// '`$ANY`': { content: { 'application/json': { schema: '`$CAPTURE`' } } },
|
|
1027
|
+
['`$SELECT`', { '$KEY': { '`$OR`': ['200', '201'] } },
|
|
1028
|
+
{ schema: '`$CAPTURE`' }],
|
|
1029
|
+
});
|
|
1030
|
+
schema = caught.schema;
|
|
1031
|
+
}
|
|
1032
|
+
if (null == schema) {
|
|
1033
|
+
why.push('no-schema');
|
|
1034
|
+
}
|
|
1035
|
+
else {
|
|
1036
|
+
if (schema.type === 'array') {
|
|
1037
|
+
why.push('array');
|
|
1038
|
+
islist = true;
|
|
1039
|
+
}
|
|
1040
|
+
if (!islist) {
|
|
1041
|
+
const properties = resolveSchemaProperties(schema);
|
|
1042
|
+
(0, jostraca_1.each)(properties, (prop) => {
|
|
1043
|
+
if (prop.type === 'array') {
|
|
1044
|
+
why.push('array-prop:' + prop.key$);
|
|
275
1045
|
islist = true;
|
|
276
1046
|
}
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
}
|
|
1047
|
+
});
|
|
1048
|
+
}
|
|
1049
|
+
if (!islist) {
|
|
1050
|
+
why.push('not-list');
|
|
1051
|
+
}
|
|
1052
|
+
}
|
|
1053
|
+
}
|
|
1054
|
+
(0, utility_1.debugpath)(pathStr, mdesc.method, 'IS-LIST', islist, why, schema);
|
|
1055
|
+
return islist;
|
|
1056
|
+
}
|
|
1057
|
+
function resolveSchemaProperties(schema) {
|
|
1058
|
+
let properties = {};
|
|
1059
|
+
// This is definitely heuristic!
|
|
1060
|
+
if (schema.allOf) {
|
|
1061
|
+
for (let i = schema.allOf.length - 1; -1 < i; --i) {
|
|
1062
|
+
properties = (0, struct_1.merge)([properties, schema.allOf[i].properties || {}]);
|
|
282
1063
|
}
|
|
283
|
-
|
|
284
|
-
|
|
1064
|
+
}
|
|
1065
|
+
if (schema.properties) {
|
|
1066
|
+
properties = (0, struct_1.merge)([properties, schema.properties]);
|
|
1067
|
+
}
|
|
1068
|
+
return properties;
|
|
1069
|
+
}
|
|
1070
|
+
function updateAction(methodName, oldParam, actionName, entityDesc, pathdesc, why) {
|
|
1071
|
+
if (
|
|
1072
|
+
// Entity not already encoding action.
|
|
1073
|
+
!entityDesc.name.endsWith((0, utility_1.canonize)(actionName))
|
|
1074
|
+
&& null == pathdesc.action[actionName]) {
|
|
1075
|
+
pathdesc.action[actionName] = {
|
|
1076
|
+
// kind: '`$BOOLEAN`',
|
|
1077
|
+
why_action: ['ent', `${entityDesc.name}`, `${why}`, `${oldParam}`, `${methodName}`]
|
|
1078
|
+
};
|
|
1079
|
+
}
|
|
1080
|
+
}
|
|
1081
|
+
function updateParamRename(ctx, data, path, method, paramRenameCapture, oldParamName, newParamName, why) {
|
|
1082
|
+
const existingNewName = paramRenameCapture.rename[oldParamName];
|
|
1083
|
+
const existingWhy = paramRenameCapture.why[oldParamName];
|
|
1084
|
+
(0, utility_1.debugpath)(path, method, 'UPDATE-PARAM-RENAME', path, oldParamName, newParamName, existingNewName);
|
|
1085
|
+
if (null == existingNewName) {
|
|
1086
|
+
paramRenameCapture.rename[oldParamName] = newParamName;
|
|
1087
|
+
if (!existingWhy.includes(why)) {
|
|
1088
|
+
existingWhy.push(why);
|
|
285
1089
|
}
|
|
1090
|
+
}
|
|
1091
|
+
else if (newParamName == existingNewName) {
|
|
1092
|
+
// if (!existingWhy.includes(why)) {
|
|
1093
|
+
// existingWhy.push(why)
|
|
286
1094
|
// }
|
|
287
1095
|
}
|
|
288
|
-
|
|
1096
|
+
else {
|
|
1097
|
+
ctx.warn({
|
|
1098
|
+
paramRenameCapture, oldParamName, newParamName, why,
|
|
1099
|
+
note: 'Param rename mismatch: existing: ' +
|
|
1100
|
+
oldParamName + ' -> ' + existingNewName + ' (why: ' + existingNewName + ') ' +
|
|
1101
|
+
' proposed: ' + newParamName + ' (why: ' + why + ') ' +
|
|
1102
|
+
'for path: ' + path + '. method: ' + method
|
|
1103
|
+
});
|
|
1104
|
+
}
|
|
289
1105
|
}
|
|
290
|
-
function
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
1106
|
+
function isParam(partStr) {
|
|
1107
|
+
return '{' === partStr[0] && '}' === partStr[partStr.length - 1];
|
|
1108
|
+
}
|
|
1109
|
+
/*
|
|
1110
|
+
function fixEntName(origName: string) {
|
|
1111
|
+
if (null == origName) {
|
|
1112
|
+
return origName
|
|
1113
|
+
}
|
|
1114
|
+
return depluralize(snakify(origName))
|
|
1115
|
+
}
|
|
1116
|
+
*/
|
|
1117
|
+
function findcmps(data, pathStr, underprops, opts) {
|
|
1118
|
+
const cmplist = [];
|
|
1119
|
+
const cmpset = new Set();
|
|
1120
|
+
// TODO: cache in ctx.work
|
|
1121
|
+
(0, jostraca_1.each)(data.def.paths[pathStr])
|
|
1122
|
+
.map((md) => {
|
|
1123
|
+
underprops.map((up) => {
|
|
1124
|
+
let found = (0, utility_1.find)(md[up], 'x-ref');
|
|
1125
|
+
found.map((xref) => {
|
|
1126
|
+
// console.log('FINDCMPS', pathStr, (md as any).key$, up, xref.val)
|
|
1127
|
+
let m = xref.val.match(/\/(components\/schemas|definitions)\/(.+)$/);
|
|
1128
|
+
if (m) {
|
|
1129
|
+
cmplist.push(m[2]);
|
|
1130
|
+
cmpset.add(m[2]);
|
|
1131
|
+
}
|
|
1132
|
+
});
|
|
1133
|
+
});
|
|
1134
|
+
});
|
|
1135
|
+
// console.log('FOUNDCMPS', cmps)
|
|
1136
|
+
return (opts?.uniq ? Array.from(cmpset) : cmplist).map(n => ({ cmp: (0, utility_1.canonize)(n), origcmp: n }));
|
|
1137
|
+
}
|
|
1138
|
+
function makeMethodEntityDesc(desc) {
|
|
1139
|
+
let ment = {
|
|
1140
|
+
cmp: desc.cmp ?? null,
|
|
1141
|
+
origcmp: desc.origcmp ?? null,
|
|
1142
|
+
origcmpref: desc.origcmpref ?? null,
|
|
1143
|
+
ref: desc.ref ?? '',
|
|
1144
|
+
why_cmp: desc.why_cmp ?? [],
|
|
1145
|
+
cmpoccur: desc.cmpoccur ?? 0,
|
|
1146
|
+
path_rate: desc.path_rate ?? 0,
|
|
1147
|
+
method_rate: desc.method_rate ?? 0,
|
|
1148
|
+
entname: desc.entname ?? '',
|
|
1149
|
+
why_op: desc.why_op ?? [],
|
|
1150
|
+
rename: desc.rename ?? { param: {} },
|
|
1151
|
+
why_rename: desc.why_rename ?? { why_param: {} },
|
|
1152
|
+
rename_orig: desc.rename_orig ?? [],
|
|
1153
|
+
opname: desc.opname ?? '',
|
|
1154
|
+
why_opname: desc.why_opname ?? [],
|
|
1155
|
+
};
|
|
1156
|
+
return ment;
|
|
1157
|
+
}
|
|
1158
|
+
function findPotentialSchemaRefs(pathStr, methodName, responses) {
|
|
1159
|
+
const xrefs = [];
|
|
1160
|
+
const rescodes = ['200', '201'];
|
|
1161
|
+
for (let rescode of rescodes) {
|
|
1162
|
+
const schema = getResponseSchema(responses[rescode]);
|
|
1163
|
+
if (null != schema) {
|
|
1164
|
+
if (null != schema['x-ref']) {
|
|
1165
|
+
xrefs.push(schema['x-ref']);
|
|
1166
|
+
}
|
|
1167
|
+
else if ('array' === schema.type && null != schema.items?.['x-ref']) {
|
|
1168
|
+
xrefs.push(schema.items?.['x-ref']);
|
|
1169
|
+
}
|
|
1170
|
+
}
|
|
295
1171
|
}
|
|
1172
|
+
(0, utility_1.debugpath)(pathStr, methodName, 'POTENTIAL-SCHEMA-REFS', xrefs);
|
|
1173
|
+
return xrefs;
|
|
1174
|
+
}
|
|
1175
|
+
function hasMethod(def, pathStr, methodName) {
|
|
1176
|
+
const pathDef = def?.paths?.[pathStr];
|
|
1177
|
+
const found = (null != pathDef
|
|
1178
|
+
&& (null != pathDef[methodName.toLowerCase()]
|
|
1179
|
+
|| null != pathDef[methodName.toUpperCase()]));
|
|
1180
|
+
console.log('hasMethod', pathStr, methodName, found);
|
|
1181
|
+
return found;
|
|
296
1182
|
}
|
|
297
1183
|
//# sourceMappingURL=heuristic01.js.map
|