Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 5424a5f

Browse files
authored
Merge pull request #15 from onmax/alpha
Alpha
2 parents c31fa75 + 233087b commit 5424a5f

File tree

19 files changed

+8076
-161
lines changed

19 files changed

+8076
-161
lines changed

‎package-lock.json‎

Lines changed: 7587 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎package.json‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "openapi-graph-core",
3-
"version": "0.0.1-alpha.2.1.0",
3+
"version": "0.0.1-alpha.3.0",
44
"description": "A TS library to manage large API projects defined by OpenAPIv3 specification.",
55
"main": "./lib/index.js",
66
"keywords": [
@@ -39,6 +39,6 @@
3939
},
4040
"dependencies": {
4141
"@apidevtools/swagger-parser": "^10.0.2",
42-
"openapi-graph-types": "0.0.1-alpha.2.1.0"
42+
"openapi-graph-types": "0.0.1-alpha.3.1"
4343
}
4444
}

‎src/analyzer/Analyzer.ts‎

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import {
2+
AnalyzerConstructor,
3+
OpenAPIGraphInterface,
4+
OpenAPIGraphsInterface,
5+
SchemaNodeInterface,
6+
} from 'openapi-graph-types';
7+
8+
export const Analyzer: AnalyzerConstructor = class AnalyzerImpl {
9+
graphs!: { [key: string]: OpenAPIGraphInterface };
10+
11+
constructor(graphs: OpenAPIGraphsInterface) {
12+
this.graphs = graphs.builder.graphs;
13+
}
14+
15+
getUnusedSchemas(): { [path: string]: SchemaNodeInterface[] } {
16+
const unusedSchemas: { [path: string]: SchemaNodeInterface[] } = {};
17+
Object.keys(this.graphs).forEach((path) => {
18+
const schemas = this.graphs[path].nodes.schemas;
19+
unusedSchemas[path] = Object.values(schemas).filter(
20+
(schema) => !schema.isInline && Object.values(schema.referencedBy).length === 0,
21+
);
22+
});
23+
return unusedSchemas;
24+
}
25+
26+
getDeprecatedSchemasBeingUsed(): { [path: string]: SchemaNodeInterface[] } {
27+
const deprecatedSchemasBeingUsed: { [path: string]: SchemaNodeInterface[] } = {};
28+
Object.keys(this.graphs).forEach((path) => {
29+
const schemas = this.graphs[path].nodes.schemas;
30+
deprecatedSchemasBeingUsed[path] = Object.values(schemas).filter(
31+
(schema) => schema.content.type !== 'array' && schema.content.deprecated,
32+
);
33+
});
34+
return deprecatedSchemasBeingUsed;
35+
}
36+
};

‎src/analyzer/index.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { Analyzer } from './Analyzer';

‎src/graph/OpenAPIGraph.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,4 +32,4 @@ export const OpenAPIGraph: OpenAPIGraphConstructor = class OpenAPIGraphImpl impl
3232
getSchemaRefEdges(): EdgesRefDict['schemaRef'] {
3333
return this.edges.ref.schemaRef;
3434
}
35-
}
35+
};

‎src/graph/OpenAPIGraphs.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ export const OpenAPIGraphs: OpenAPIGraphsConstructor = class OpenAPIGraphsImpl i
1111
}
1212

1313
async build() {
14-
const apis = await fetcher(this.rootPath)
14+
const apis = await fetcher(this.rootPath);
1515
this.builder = new OpenAPIGraphsBuilder(apis);
1616
}
17-
}
17+
};
Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,31 @@
11
import { getRefEdges, getSchemaNodes, resolveReference } from '.';
2-
import { EdgesRefDict, Nodes, OpenAPIContent, OpenAPIGraphInterface, OpenAPIGraphsBuilderConstructor, OpenAPIGraphsBuilderInterface } from 'openapi-graph-types';
2+
import {
3+
EdgesRefDict,
4+
Nodes,
5+
OpenAPIContent,
6+
OpenAPIGraphInterface,
7+
OpenAPIGraphsBuilderConstructor,
8+
OpenAPIGraphsBuilderInterface,
9+
} from 'openapi-graph-types';
310
import { OpenAPIGraph } from '../OpenAPIGraph';
411

5-
export const OpenAPIGraphsBuilder: OpenAPIGraphsBuilderConstructor = class OpenAPIGraphsBuilderImpl implements OpenAPIGraphsBuilderInterface {
6-
graphs: OpenAPIGraphInterface[];
12+
export const OpenAPIGraphsBuilder: OpenAPIGraphsBuilderConstructor = class OpenAPIGraphsBuilderImpl
13+
implements OpenAPIGraphsBuilderInterface {
14+
graphs: OpenAPIGraphsBuilderInterface['graphs'];
715

816
constructor(apis: OpenAPIContent[]) {
917
this.graphs = this.initializeGraph(apis);
1018
}
1119

12-
private initializeGraph(apis: OpenAPIContent[]): OpenAPIGraphInterface[] {
13-
const graphs: OpenAPIGraphInterface[] = [];
20+
private initializeGraph(apis: OpenAPIContent[]): OpenAPIGraphsBuilderInterface['graphs'] {
21+
const graphs: OpenAPIGraphsBuilderInterface['graphs'] = {};
1422
apis.forEach((api) => {
1523
const graph: OpenAPIGraphInterface = new OpenAPIGraph(api.path);
1624
graph.setSchemaNodes(this.getSchemaNodes(api));
17-
graphs.push(graph);
25+
graphs[api.path]=graph;
1826
});
19-
graphs.map((graph, i) => {
20-
graph.setRefEdges(this.getRefEdges(graphs, apis[i]));
27+
Object.keys(graphs).map((graphKey, i) => {
28+
graphs[graphKey].setRefEdges(this.getRefEdges(graphs, apis[i]));
2129
});
2230
return graphs;
2331
}
@@ -26,8 +34,8 @@ export const OpenAPIGraphsBuilder: OpenAPIGraphsBuilderConstructor = class OpenA
2634
return getSchemaNodes(api.content);
2735
}
2836

29-
private getRefEdges(graphs: OpenAPIGraphInterface[], api: OpenAPIContent): EdgesRefDict {
37+
private getRefEdges(graphs: OpenAPIGraphsBuilderInterface['graphs'], api: OpenAPIContent): EdgesRefDict {
3038
const edges: EdgesRefDict = getRefEdges(api.content, api.path);
3139
return resolveReference(graphs, edges);
3240
}
33-
}
41+
};

‎src/graph/builder/finder.ts‎

Lines changed: 76 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,84 @@
1+
import { EdgesRefDict, Nodes, OpenAPIGraphsBuilderInterface, SchemaNodeInterface } from 'openapi-graph-types';
12
import { OpenAPIV3 } from 'openapi-types';
2-
import { EdgesRefDict, Nodes, OpenAPIGraphInterface } from 'openapi-graph-types';
33
import { SchemaNode } from '../../graph/nodes/SchemaNode';
44
import { RefEdge } from '../edges';
55

6+
/**
7+
* Creates a new Schema depends on its type. Swagger schemas can be Array or NonArray
8+
*
9+
* @param schema source
10+
* @param name The given name for the schema
11+
* @returns the new schema instance
12+
*/
13+
function createSchema(
14+
schemaNodes: { [key: string]: SchemaNodeInterface },
15+
schema: OpenAPIV3.SchemaObject | OpenAPIV3.ReferenceObject,
16+
schemaName: string,
17+
isInline: boolean,
18+
) {
19+
if (schema && !('$ref' in schema)) {
20+
if ('type' in schema && schema?.type === 'array') {
21+
schemaNodes[schemaName] = new SchemaNode(schemaName, schema, isInline);
22+
} else {
23+
schemaNodes[schemaName] = new SchemaNode(schemaName, schema, isInline);
24+
}
25+
}
26+
}
27+
28+
export function getDefinedSchemasNodes(api: OpenAPIV3.Document): { [key: string]: SchemaNodeInterface } {
29+
const nodes: { [key: string]: SchemaNodeInterface } = {};
30+
const schemas: { [key: string]: OpenAPIV3.ReferenceObject | OpenAPIV3.SchemaObject } | undefined =
31+
api.components?.schemas;
32+
if (schemas) {
33+
Object.keys(schemas).forEach((schemaName) => createSchema(nodes, schemas[schemaName], schemaName, false));
34+
}
35+
return nodes;
36+
}
37+
638
/**
739
* It will find all the schemas defined in the specification
840
*
941
* @param api source
1042
* @param fn callback which will be executed for every node
1143
*/
12-
export function getSchemaNodes(apiContent: OpenAPIV3.Document): Nodes['schemas'] {
13-
const nodes: Nodes['schemas'] = {};
14-
const schemas = apiContent?.components?.schemas;
15-
if (schemas) {
16-
Object.keys(schemas).forEach((schema) => {
17-
if ('$ref' !== schema) {
18-
nodes[schema] = new SchemaNode(schema, schemas[schema] as OpenAPIV3.SchemaObject);
19-
}
20-
});
44+
export function getInlineSchemasNodes(
45+
json: any,
46+
currentIndex = 1,
47+
nodes: { [key: string]: SchemaNodeInterface } = {},
48+
): { [key: string]: SchemaNodeInterface } {
49+
const schema: OpenAPIV3.SchemaObject | undefined = json?.schema;
50+
if (schema) {
51+
createSchema(nodes, schema, `inline-schema-${currentIndex++}`, true);
52+
if (schema?.type === 'array') {
53+
getInlineSchemasNodes(json.items, currentIndex, nodes);
54+
}
55+
} else if (json) {
56+
function handleJson() {
57+
Object.keys(json).forEach((key) => {
58+
nodes = getInlineSchemasNodes(json[key], currentIndex, nodes);
59+
});
60+
}
61+
if ({}.constructor === json.constructor) {
62+
handleJson();
63+
} else if ([].constructor === json.constructor) {
64+
json.forEach(handleJson);
65+
}
2166
}
2267
return nodes;
2368
}
2469

70+
export function getSchemaNodes(api: OpenAPIV3.Document): Nodes['schemas'] {
71+
return { ...getDefinedSchemasNodes(api), ...getInlineSchemasNodes(api) };
72+
}
73+
2574
/**
2675
* It will find all the references defined in the specification
2776
*
2877
* @param api source
2978
* @param fn callback which will be executed for every node
3079
*/
3180
export function getRefEdges(json: any, absolutePath: string, edges: EdgesRefDict = { schemaRef: {} }): EdgesRefDict {
32-
/* tslint:disable:no-string-literal */
33-
const ref: string = json['$ref'];
81+
const ref: string | undefined = json?.$ref;
3482
if (ref) {
3583
// TODO Should test any type of component
3684
if (/components\/schemas/.test(ref)) {
@@ -39,7 +87,7 @@ export function getRefEdges(json: any, absolutePath: string, edges: EdgesRefDict
3987
} else {
4088
function handleJson() {
4189
Object.keys(json).forEach((key) => {
42-
edges=getRefEdges(json[key], absolutePath, edges);
90+
getRefEdges(json[key], absolutePath, edges);
4391
});
4492
}
4593
if ({}.constructor === json.constructor) {
@@ -51,17 +99,26 @@ export function getRefEdges(json: any, absolutePath: string, edges: EdgesRefDict
5199
return edges;
52100
}
53101

54-
export function resolveReference(graphs: OpenAPIGraphInterface[], refs: EdgesRefDict): EdgesRefDict {
102+
/**
103+
* Sets the edges' child value
104+
* @param graphs
105+
* @param refs
106+
* @returns
107+
*/
108+
export function resolveReference(graphs: OpenAPIGraphsBuilderInterface['graphs'], refs: EdgesRefDict): EdgesRefDict {
55109
const filteredRefs: EdgesRefDict = {
56110
schemaRef: {},
57111
};
58112

59113
Object.values(refs.schemaRef)
60-
.map((r) => ({ r, g: graphs.find((g) => g.path === r.absolutePath) }))
61-
.filter((o) => o.g?.nodes.schemas[o.r.tokenName])
62-
.forEach((o) => {
63-
o.r.child = o.g?.nodes.schemas[o.r.tokenName];
64-
filteredRefs.schemaRef[o.r.getFullPath()] = o.r;
114+
.filter((r) => graphs?.[r.refToFilePath].nodes.schemas[r.tokenName])
115+
.forEach((r) => {
116+
const schema = graphs?.[r.refToFilePath].nodes.schemas[r.tokenName];
117+
if (schema) {
118+
schema.referencedBy[r.path] = r;
119+
r.child = schema;
120+
filteredRefs.schemaRef[r.path] = r;
121+
}
65122
});
66123

67124
return filteredRefs;

‎src/graph/edges/Edge.ts‎

Lines changed: 51 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,55 @@
1-
import { EdgeConstructor, EdgeInterface, NodeInterface } from 'openapi-graph-types';
1+
import { EdgeConstructor, EdgeInterface, NodeInterface, RefType } from 'openapi-graph-types';
2+
import { resolve } from 'path';
23

34
export const Edge: EdgeConstructor = class EdgeImpl implements EdgeInterface {
45
parent: NodeInterface | undefined;
56
child: NodeInterface | undefined;
6-
}
7+
8+
rawPath!: string;
9+
tokenName!: string;
10+
specificationPath!: string;
11+
filePath!: string;
12+
refToFilePath!: string;
13+
14+
constructor(filePath: string, specificationPath: string) {
15+
// TODO: Check that inputs are valid
16+
this.filePath = filePath;
17+
this.rawPath = specificationPath;
18+
19+
const cleanSpecificationPathParts = specificationPath?.split('#');
20+
if (cleanSpecificationPathParts.length < 2) {
21+
return;
22+
}
23+
this.specificationPath = cleanSpecificationPathParts[1];
24+
switch (this.type) {
25+
case RefType.Local:
26+
case RefType.URL:
27+
this.refToFilePath = resolve(`${filePath}/${cleanSpecificationPathParts[0]}`);
28+
break;
29+
case RefType.Remote:
30+
this.refToFilePath = resolve(`${filePath}/../${cleanSpecificationPathParts[0]}`);
31+
break;
32+
}
33+
const specificationPathParts = specificationPath.split('/');
34+
this.tokenName = specificationPathParts[specificationPathParts.length - 1];
35+
}
36+
37+
get path(): string {
38+
return `${this.filePath}#${this.specificationPath}`;
39+
}
40+
41+
// TODO Tests
42+
get type(): RefType | undefined {
43+
// Test if extension file is in the string
44+
if (/\.(yml|yaml|json)\/?#\//.test(this.rawPath)) {
45+
if (/^https?:\/\//.test(this.rawPath)) {
46+
return RefType.URL;
47+
} else {
48+
return RefType.Remote;
49+
}
50+
} else if (!/\.(yml|yaml|json)\/?#\//.test(this.rawPath)) {
51+
return RefType.Local;
52+
}
53+
return undefined;
54+
}
55+
};

‎src/graph/edges/InlineRefEdge.ts‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { RefEdgeConstructor, RefEdgeInterface } from 'openapi-graph-types';
2+
import { Edge } from '.';
3+
4+
export const RefEdge: RefEdgeConstructor = class RefEdgeImpl extends Edge implements RefEdgeInterface {
5+
constructor(absolutePath: string, ref: string) {
6+
super(absolutePath, ref);
7+
}
8+
};

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /