Navigation Menu

Skip to content

Commit

Permalink
Add linter plugin to check for array responses
Browse files Browse the repository at this point in the history
  • Loading branch information
Mike Kistler committed Feb 15, 2018
1 parent 3b40d65 commit 922394f
Show file tree
Hide file tree
Showing 5 changed files with 139 additions and 0 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Expand Up @@ -7,6 +7,10 @@
# Swift
.build
Packages
# Node
node_modules
package-lock.json
bundle.json
# vi
*.swp
# vscode
Expand Down
19 changes: 19 additions & 0 deletions linters/node/gnostic-lint-responses/Makefile
@@ -0,0 +1,19 @@

GNOSTIC = $(GOPATH)/src/github.com/googleapis/gnostic

plugin:
node_modules/.bin/pbjs -t json \
$(GNOSTIC)/OpenAPIv2/OpenAPIv2.proto \
$(GNOSTIC)/OpenAPIv3/OpenAPIv3.proto \
$(GNOSTIC)/discovery/discovery.proto \
$(GNOSTIC)/surface/surface.proto \
$(GNOSTIC)/plugins/plugin.proto \
> bundle.json
node_modules/.bin/nexe gnostic-lint-responses.js
mv gnostic-lint-responses $(GOPATH)/bin

run: plugin
gnostic $(GNOSTIC)/examples/v2.0/yaml/petstore.yaml --lint-responses

setup:
npm install
15 changes: 15 additions & 0 deletions linters/node/gnostic-lint-responses/README.md
@@ -0,0 +1,15 @@
This directory contains a gnostic linter written with node.

It is built using [dcodeIO/Protobuf.js](https://github.com/dcodeIO/ProtoBuf.js).

### SETUP

- Install node.
- Run `make setup` to install node dependencies.

### TRY IT

- Run `make run` to test-run the plugin.



83 changes: 83 additions & 0 deletions linters/node/gnostic-lint-responses/gnostic-lint-responses.js
@@ -0,0 +1,83 @@
// import libraries
const protobuf = require("protobufjs");
const getStdin = require("get-stdin");
const find = require("lodash/find");
const forEach = require("lodash/forEach");
const pick = require("lodash/pick");

// import messages
const root = protobuf.Root.fromJSON(require("./bundle.json"));
const Request = root.lookupType("gnostic.plugin.v1.Request");
const Response = root.lookupType("gnostic.plugin.v1.Response");
const Document = root.lookupType("openapi.v2.Document");

getStdin.buffer().then(buffer => {
const request = Request.decode(buffer);
var messages = [];
for (var j in request.models) {
const m = request.models[j];
if (m.type_url == "openapi.v2.Document") {
const openapi2 = Document.decode(m.value);
const paths = openapi2.paths.path;
for (var i in paths) {
const path = paths[i];
// console.error('path %s\n\n', path.name)

// Arrays MUST NOT be returned as the top-level structure in a response body.
let pathOps = pick(path.value, ["get","head","post", "put", "patch", "delete", "options"]);
forEach(pathOps, (op, opKey) => {
if (op != null) {
forEach(op.responses.responseCode, responseObj => {
// console.error('responseObj is %j', responseObj)
name = responseObj.name;
response = responseObj.value.response;
if (response.schema && response.schema.schema) {
if (!response.schema.schema._ref) {
if (
response.schema.schema.type != null &&
response.schema.schema.type.value == "array"
) {
messages.push({
level: 3,
code: "NO_ARRAY_RESPONSES",
text: "Arrays MUST NOT be returned as the top-level structure in a response body.",
keys: ["paths", path.name, opKey, "responses", name, "schema"]
});
}
} else {
let schemaName = response.schema.schema._ref.match(/#\/definitions\/(\w+)/);
if (schemaName) {
const definitions = openapi2.definitions.additionalProperties;
const schemaKvp = find(definitions, {name: schemaName[1]
});
//console.error('schemaKvp.value.type = %s', schemaKvp.value.type.value)
if (schemaKvp && schemaKvp.value.type && schemaKvp.value.type.value.indexOf("array") >= 0) {
messages.push({
level: 3,
code: "NO_ARRAY_RESPONSES",
text: "Arrays MUST NOT be returned as the top-level structure in a response body.",
keys: ["paths", path.name, opKey, "responses", name, "schema" ]
});
}
}
}
}
});
}
});
}
}
}

const payload = {
messages: messages
};

// Verify the payload if necessary (i.e. when possibly incomplete or invalid)
const errMsg = Response.verify(payload);
if (errMsg) throw Error(errMsg);

const message = Response.create(payload);
process.stdout.write(Response.encode(message).finish());
})
.catch(err => console.error(err));
18 changes: 18 additions & 0 deletions linters/node/gnostic-lint-responses/package.json
@@ -0,0 +1,18 @@
{
"name": "gnostic-lint-responses",
"version": "1.0.0",
"description": "Gnostic linter plugin to check responses",
"main": "gnostic-lint-responses.js",
"dependencies": {
"get-stdin": "^5.0.1",
"lodash": "^4.17.5",
"nexe": "^2.0.0-rc.24",
"protobufjs": "^6.8.4"
},
"devDependencies": {},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC"
}

0 comments on commit 922394f

Please sign in to comment.