docs
0
# Analysis and Metrics
1
2
API definition analysis, feature detection, and complexity measurement tools for OpenAPI definitions.
3
4
## Capabilities
5
6
### Analyze OpenAPI Definition
7
8
Comprehensive analysis of OpenAPI definitions to detect features, complexity, and usage patterns.
9
10
```typescript { .api }
11
/**
12
* Analyze an OpenAPI definition for features and complexity
13
* @param definition - OpenAPI definition to analyze
14
* @returns Promise resolving to detailed analysis results
15
*/
16
function analyzer(definition: OASDocument): Promise<OASAnalysis>;
17
```
18
19
**Usage Examples:**
20
21
```typescript
22
import analyzer from "oas/analyzer";
23
24
const definition = {
25
openapi: "3.1.0",
26
info: { title: "My API", version: "1.0.0" },
27
paths: { /* ... */ }
28
};
29
30
const analysis = await analyzer(definition);
31
32
// General metrics
33
console.log(`Operations: ${analysis.general.operationTotal.found}`);
34
console.log(`File size: ${analysis.general.rawFileSize.found} bytes`);
35
console.log(`Media types: ${analysis.general.mediaTypes.found.join(', ')}`);
36
console.log(`Security types: ${analysis.general.securityTypes.found.join(', ')}`);
37
38
// OpenAPI feature usage
39
console.log(`Uses callbacks: ${analysis.openapi.callbacks.present}`);
40
console.log(`Uses webhooks: ${analysis.openapi.webhooks.present}`);
41
console.log(`Has circular refs: ${analysis.openapi.circularRefs.present}`);
42
43
// ReadMe extension usage
44
console.log(`Custom code samples: ${analysis.readme['x-readme.code-samples'].present}`);
45
console.log(`Static headers: ${analysis.readme['x-readme.headers'].present}`);
46
```
47
48
### Analysis Result Structure
49
50
```typescript { .api }
51
interface OASAnalysis {
52
/** General API metrics and statistics */
53
general: {
54
dereferencedFileSize: OASAnalysisGeneral;
55
mediaTypes: OASAnalysisGeneral;
56
operationTotal: OASAnalysisGeneral;
57
rawFileSize: OASAnalysisGeneral;
58
securityTypes: OASAnalysisGeneral;
59
};
60
/** OpenAPI specification feature usage */
61
openapi: {
62
additionalProperties: OASAnalysisFeature;
63
callbacks: OASAnalysisFeature;
64
circularRefs: OASAnalysisFeature;
65
commonParameters: OASAnalysisFeature;
66
discriminators: OASAnalysisFeature;
67
links: OASAnalysisFeature;
68
style: OASAnalysisFeature;
69
polymorphism: OASAnalysisFeature;
70
serverVariables: OASAnalysisFeature;
71
webhooks: OASAnalysisFeature;
72
xml: OASAnalysisFeature;
73
xmlSchemas: OASAnalysisFeature;
74
xmlRequests: OASAnalysisFeature;
75
xmlResponses: OASAnalysisFeature;
76
};
77
/** ReadMe-specific extension usage */
78
readme: {
79
'x-default': OASAnalysisFeature;
80
'x-readme.code-samples': OASAnalysisFeature;
81
'x-readme.headers': OASAnalysisFeature;
82
'x-readme.explorer-enabled': OASAnalysisFeature;
83
'x-readme.proxy-enabled': OASAnalysisFeature;
84
'x-readme.samples-languages': OASAnalysisFeature;
85
'x-readme-ref-name': OASAnalysisFeature;
86
'x-readme.samples-enabled'?: OASAnalysisFeature;
87
raw_body?: OASAnalysisFeature;
88
};
89
}
90
91
interface OASAnalysisFeature {
92
/** Whether the feature is present in the definition */
93
present: boolean;
94
/** JSON pointer locations where the feature is used */
95
locations: string[];
96
}
97
98
interface OASAnalysisGeneral {
99
/** Feature name for reporting */
100
name: string;
101
/** Found values (strings for enums, numbers for counts) */
102
found: string[] | number;
103
}
104
```
105
106
## Feature Analysis Details
107
108
### General Metrics Analysis
109
110
```typescript
111
const analysis = await analyzer(definition);
112
113
// File size analysis
114
const rawSize = analysis.general.rawFileSize.found as number;
115
const dereferencedSize = analysis.general.dereferencedFileSize.found as number;
116
const expansion = dereferencedSize - rawSize;
117
118
console.log(`Raw file size: ${rawSize} bytes`);
119
console.log(`Dereferenced size: ${dereferencedSize} bytes`);
120
console.log(`Size expansion: ${expansion} bytes (${(expansion/rawSize*100).toFixed(1)}%)`);
121
122
// Operation complexity
123
const opCount = analysis.general.operationTotal.found as number;
124
console.log(`Total operations: ${opCount}`);
125
126
if (opCount > 50) {
127
console.log("Large API - consider using tags for organization");
128
} else if (opCount > 100) {
129
console.log("Very large API - recommend splitting or using reduction");
130
}
131
132
// Media type diversity
133
const mediaTypes = analysis.general.mediaTypes.found as string[];
134
console.log(`Supported media types: ${mediaTypes.length}`);
135
136
if (mediaTypes.includes('application/xml')) {
137
console.log("XML support detected");
138
}
139
if (mediaTypes.some(type => type.includes('multipart'))) {
140
console.log("File upload support detected");
141
}
142
```
143
144
### OpenAPI Feature Detection
145
146
```typescript
147
// Advanced feature usage
148
if (analysis.openapi.callbacks.present) {
149
console.log("API uses callbacks:");
150
analysis.openapi.callbacks.locations.forEach(location => {
151
console.log(` Found at: ${location}`);
152
});
153
}
154
155
if (analysis.openapi.webhooks.present) {
156
console.log("API defines webhooks (OpenAPI 3.1):");
157
analysis.openapi.webhooks.locations.forEach(location => {
158
console.log(` Webhook at: ${location}`);
159
});
160
}
161
162
if (analysis.openapi.circularRefs.present) {
163
console.log("⚠️ Circular references detected:");
164
analysis.openapi.circularRefs.locations.forEach(location => {
165
console.log(` Circular ref: ${location}`);
166
});
167
}
168
169
// Polymorphism usage
170
if (analysis.openapi.polymorphism.present) {
171
console.log("Polymorphic schemas detected:");
172
analysis.openapi.polymorphism.locations.forEach(location => {
173
console.log(` Polymorphism at: ${location}`);
174
});
175
}
176
177
// Parameter serialization complexity
178
if (analysis.openapi.style.present) {
179
console.log("Custom parameter serialization styles:");
180
analysis.openapi.style.locations.forEach(location => {
181
console.log(` Custom style at: ${location}`);
182
});
183
}
184
```
185
186
### ReadMe Extension Analysis
187
188
```typescript
189
// Documentation enhancements
190
if (analysis.readme['x-readme.code-samples'].present) {
191
console.log("Custom code samples defined:");
192
analysis.readme['x-readme.code-samples'].locations.forEach(location => {
193
console.log(` Code sample at: ${location}`);
194
});
195
}
196
197
if (analysis.readme['x-readme.headers'].present) {
198
console.log("Static headers configured:");
199
analysis.readme['x-readme.headers'].locations.forEach(location => {
200
console.log(` Static headers at: ${location}`);
201
});
202
}
203
204
// API Explorer configuration
205
if (analysis.readme['x-readme.explorer-enabled'].present) {
206
console.log("API Explorer settings found");
207
}
208
209
if (analysis.readme['x-readme.proxy-enabled'].present) {
210
console.log("CORS proxy configuration found");
211
}
212
213
// Language preferences
214
if (analysis.readme['x-readme.samples-languages'].present) {
215
console.log("Custom code sample languages configured");
216
}
217
```
218
219
## Advanced Analysis Patterns
220
221
### Complexity Scoring
222
223
```typescript
224
function calculateComplexityScore(analysis: OASAnalysis): number {
225
let score = 0;
226
227
// Base complexity from operation count
228
const opCount = analysis.general.operationTotal.found as number;
229
score += opCount;
230
231
// Feature complexity multipliers
232
if (analysis.openapi.callbacks.present) score += 10;
233
if (analysis.openapi.webhooks.present) score += 8;
234
if (analysis.openapi.circularRefs.present) score += 15;
235
if (analysis.openapi.polymorphism.present) score += 12;
236
if (analysis.openapi.discriminators.present) score += 8;
237
if (analysis.openapi.links.present) score += 6;
238
239
// Media type diversity
240
const mediaTypes = analysis.general.mediaTypes.found as string[];
241
score += mediaTypes.length * 2;
242
243
// Security complexity
244
const securityTypes = analysis.general.securityTypes.found as string[];
245
score += securityTypes.length * 3;
246
247
return score;
248
}
249
250
const complexity = calculateComplexityScore(analysis);
251
console.log(`API complexity score: ${complexity}`);
252
253
if (complexity > 100) {
254
console.log("High complexity API - recommend comprehensive testing");
255
} else if (complexity > 200) {
256
console.log("Very high complexity - consider refactoring or splitting");
257
}
258
```
259
260
### Feature Recommendations
261
262
```typescript
263
function generateRecommendations(analysis: OASAnalysis): string[] {
264
const recommendations: string[] = [];
265
266
// File size recommendations
267
const rawSize = analysis.general.rawFileSize.found as number;
268
if (rawSize > 1000000) { // > 1MB
269
recommendations.push("Consider splitting large API definition into multiple files");
270
}
271
272
// Missing features that could improve API
273
if (!analysis.openapi.commonParameters.present) {
274
recommendations.push("Consider using common parameters to reduce duplication");
275
}
276
277
if (!analysis.readme['x-readme.code-samples'].present) {
278
recommendations.push("Add custom code samples for better developer experience");
279
}
280
281
// Complexity warnings
282
if (analysis.openapi.circularRefs.present) {
283
recommendations.push("Review circular references - they may indicate design issues");
284
}
285
286
// XML-specific recommendations
287
if (analysis.openapi.xml.present && !analysis.openapi.xmlSchemas.present) {
288
recommendations.push("XML usage detected but no XML schemas defined");
289
}
290
291
return recommendations;
292
}
293
294
const recommendations = generateRecommendations(analysis);
295
console.log("\nRecommendations:");
296
recommendations.forEach(rec => console.log(`• ${rec}`));
297
```
298
299
### Comparison Analysis
300
301
```typescript
302
// Compare two API versions
303
async function compareAPIs(oldDef: OASDocument, newDef: OASDocument) {
304
const [oldAnalysis, newAnalysis] = await Promise.all([
305
analyzer(oldDef),
306
analyzer(newDef)
307
]);
308
309
// Operation count changes
310
const oldOps = oldAnalysis.general.operationTotal.found as number;
311
const newOps = newAnalysis.general.operationTotal.found as number;
312
const opChange = newOps - oldOps;
313
314
console.log(`Operations: ${oldOps} → ${newOps} (${opChange >= 0 ? '+' : ''}${opChange})`);
315
316
// New features added
317
const features = [
318
'callbacks', 'webhooks', 'discriminators', 'polymorphism', 'links'
319
] as const;
320
321
features.forEach(feature => {
322
const oldHas = oldAnalysis.openapi[feature].present;
323
const newHas = newAnalysis.openapi[feature].present;
324
325
if (!oldHas && newHas) {
326
console.log(`✨ Added ${feature} support`);
327
} else if (oldHas && !newHas) {
328
console.log(`❌ Removed ${feature} support`);
329
}
330
});
331
332
// File size impact
333
const oldSize = oldAnalysis.general.rawFileSize.found as number;
334
const newSize = newAnalysis.general.rawFileSize.found as number;
335
const sizeChange = ((newSize - oldSize) / oldSize * 100).toFixed(1);
336
337
console.log(`File size change: ${sizeChange}%`);
338
}
339
```
340
341
## Integration Examples
342
343
### API Documentation Pipeline
344
345
```typescript
346
// Generate documentation metadata from analysis
347
async function generateDocMetadata(definition: OASDocument) {
348
const analysis = await analyzer(definition);
349
350
return {
351
complexity: calculateComplexityScore(analysis),
352
features: {
353
hasCallbacks: analysis.openapi.callbacks.present,
354
hasWebhooks: analysis.openapi.webhooks.present,
355
hasCircularRefs: analysis.openapi.circularRefs.present,
356
supportsXML: analysis.openapi.xml.present,
357
customCodeSamples: analysis.readme['x-readme.code-samples'].present
358
},
359
metrics: {
360
operationCount: analysis.general.operationTotal.found,
361
mediaTypes: analysis.general.mediaTypes.found,
362
securityTypes: analysis.general.securityTypes.found
363
},
364
recommendations: generateRecommendations(analysis)
365
};
366
}
367
```
368
369
### Quality Gate Integration
370
371
```typescript
372
// Use analysis for CI/CD quality gates
373
async function validateAPIQuality(definition: OASDocument): Promise<boolean> {
374
const analysis = await analyzer(definition);
375
376
// Quality criteria
377
const hasCircularRefs = analysis.openapi.circularRefs.present;
378
const complexity = calculateComplexityScore(analysis);
379
const opCount = analysis.general.operationTotal.found as number;
380
381
// Fail if quality issues detected
382
if (hasCircularRefs) {
383
console.error("❌ Quality gate failed: Circular references detected");
384
return false;
385
}
386
387
if (complexity > 300) {
388
console.error(`❌ Quality gate failed: Complexity too high (${complexity})`);
389
return false;
390
}
391
392
if (opCount > 150) {
393
console.error(`❌ Quality gate failed: Too many operations (${opCount})`);
394
return false;
395
}
396
397
console.log("✅ API quality gate passed");
398
return true;
399
}
400
```