Repository: Redocly/openapi-cli
Pull request #155 (2020-07-06 08:00:30)

feat: add missing rewrite rules


Patch:
diff --git a/src/rules/common/paths-identical.ts b/src/rules/common/paths-identical.ts
new file mode 100644
index 00000000..b7725a2d
--- /dev/null
+++ b/src/rules/common/paths-identical.ts
@@ -0,0 +1,22 @@
+import { Oas3Rule, Oas2Rule } from '../../visitors';
+import { UserContext } from '../../walk';
+import { Oas3Paths } from '../../typings/openapi';
+
+export const PathsIdentical: Oas3Rule | Oas2Rule = () => {
+  return {
+    PathMap(pathMap: Oas3Paths, { report, location }: UserContext) {
+      const pathsSet = new Set<string>();
+      for (const pathName of Object.keys(pathMap)){ 
+        const name = pathName.replace(/{.+}/, '{VARIABLE}');
+        if (pathsSet.has(name)) {
+          report({
+            message: 'The path already exists which differs only by path parameter name(s).',
+            location: location.child([pathName]).key(),
+          })
+        } else {
+          pathsSet.add(name);
+        }
+      }
+    }
+  };
+};
diff --git a/src/rules/common/servers-defined-variables.ts b/src/rules/common/servers-defined-variables.ts
new file mode 100644
index 00000000..39b62dd3
--- /dev/null
+++ b/src/rules/common/servers-defined-variables.ts
@@ -0,0 +1,30 @@
+import { Oas3Rule, Oas2Rule } from '../../visitors';
+import { UserContext } from '../../walk';
+import { Oas3Server } from '../../typings/openapi';
+
+export const ServersDefinedVariables: Oas3Rule | Oas2Rule = () => {
+  return {
+    Server(server:Oas3Server, { report, location }: UserContext) {
+      const urlVariables = server.url.match(/{[^}]+}/g)?.map(e => e.slice(1, e.length - 1)) || [];
+      const definedVariables = server?.variables && Object.keys(server.variables) || [];
+      
+      for (const pathVar of urlVariables) {
+        if (!definedVariables.includes(pathVar)) {
+          report({
+            message: `The "${pathVar}" variable is not defined in the "variables" objects.`,
+            location: location.child(['url'])
+          })
+        }
+      }
+
+      for (const definedVar of definedVariables) {
+        if (!urlVariables.includes(definedVar)) {
+          report({
+            message: `The "${definedVar}" variable is not used in the server's "url" field.`,
+            location: location.child(['variables', definedVar]).key(),
+          })
+        }
+      }
+    }
+  };
+};
diff --git a/src/rules/oas3/index.ts b/src/rules/oas3/index.ts
index 429e615e..7d384d69 100644
--- a/src/rules/oas3/index.ts
+++ b/src/rules/oas3/index.ts
@@ -31,6 +31,8 @@ import { PathsKebabCase } from '../common/paths-kebab-case';
 import { PathHttpVerbsOrder } from '../common/path-http-verbs-order';
 import { NoEmptyServers } from './no-empty-servers';
 import { ValidContentExamples } from './no-invalid-media-type-examples';
+import { PathsIdentical } from '../common/paths-identical';
+import { ServersDefinedVariables } from '../common/servers-defined-variables';
 
 export const rules = {
   'info-description': InfoDescription,
@@ -65,6 +67,8 @@ export const rules = {
   'boolean-parameter-prefixes': BooleanParameterPrefixes,
   'path-http-verbs-order': PathHttpVerbsOrder,
   'no-invalid-media-type-examples': ValidContentExamples,
+  'paths-identical': PathsIdentical,
+  'servers-defined-variables': ServersDefinedVariables,
   spec: OasSpec,
 } as Oas3RuleSet;
 


Test patch:
diff --git a/src/rules/oas3/__tests__/schema/paths.test.ts b/src/rules/oas3/__tests__/schema/paths.test.ts
index 53f772aa..b87c1b73 100644
--- a/src/rules/oas3/__tests__/schema/paths.test.ts
+++ b/src/rules/oas3/__tests__/schema/paths.test.ts
@@ -125,8 +125,7 @@ describe('OpenAPI Schema', () => {
     `);
   });
 
-  // TODO: should be a separate rule
-  it.skip('should report if paths are considered identical and invalid', async () => {
+  it('should report if paths are considered identical and invalid', async () => {
     const source = outdent`
       openapi: 3.0.2
       info:
@@ -137,13 +136,13 @@ describe('OpenAPI Schema', () => {
         - url: http://google.com
 
       paths:
-        '/pets/{petId}':
+        /pets/{petId}:
           get:
             responses:
               '200':
                 description: example description
-        '/pets/{name}':
-           get:
+        /pets/{name}:
+          get:
             responses:
               '200':
                 description: example description
@@ -151,9 +150,16 @@ describe('OpenAPI Schema', () => {
 
     expect(
       await validateDoc(source, {
-        spec: 'error',
+        'paths-identical': 'error',
       }),
-    ).toMatchInlineSnapshot(`Array []`);
+    ).toMatchInlineSnapshot(`
+      Array [
+        Object {
+          "location": "#/paths/~1pets~1{name}",
+          "message": "The path already exists which differs only by path parameter name(s).",
+        },
+      ]
+    `);
   });
 
   it('should not report valid matching URLs', async () => {
diff --git a/src/rules/oas3/__tests__/schema/servers.test.ts b/src/rules/oas3/__tests__/schema/servers.test.ts
index 163d01fa..dc261f4c 100644
--- a/src/rules/oas3/__tests__/schema/servers.test.ts
+++ b/src/rules/oas3/__tests__/schema/servers.test.ts
@@ -288,8 +288,7 @@ describe('OpenAPI Schema', () => {
     `);
   });
 
-  // TODO: should be a separate rule
-  it.skip('should report if some variable is not defined', async () => {
+  it('should report if some variable is not defined', async () => {
     const source = outdent`
       openapi: 3.0.2
       info:
@@ -317,9 +316,16 @@ describe('OpenAPI Schema', () => {
 
     expect(
       await validateDoc(source, {
-        spec: 'error',
+        'servers-defined-variables': 'error',
       }),
-    ).toMatchInlineSnapshot(`Array []`);
+    ).toMatchInlineSnapshot(`
+      Array [
+        Object {
+          "location": "#/servers/0/url",
+          "message": "The \\"basePath\\" variable is not defined in the \\"variables\\" objects.",
+        },
+      ]
+    `);
   });
 
   it('should report if default value is not provided in variables', async () => {