Enforcing API Design
Rules for High-Quality
Code Generation
Mike Kistler, IBM
Tim Burks, Google
The API Economy
API
API API
API
Open API
Industry standard format for describing for REST APIs
Open API Tooling
Originally designed for documentation, tools are now available to automate or assist
with many common tasks:
● API Authoring
● Validation
● Documentation
● Testing
● Mocking
● Management
● Code Generation
● Validation
OpenAPI-based Tooling at IBM
● SDK Generation for Watson Developer Cloud
○ Java
○ Node
○ Python
○ Swift
○ .NET
● Benefits
○ Faster time to Market
○ Improved Quality
○ Enforce Compliance
○ Enforce Consistency
○ Increase Adoption
https://github.com/watson-developer-cloud
Some challenges for OpenAPI-based tools
● Some elements of an OAS API definition are "optional" or "flexible"
○ Parameter and property type and format
○ OperationId
○ Response schema
● Some styles of definitions are problematic for tools
○ Inline responses
○ Content-type, accept-type
● Focus implementation effort on main use cases
Parameter and property type and format
● OpenAPI v2 does not restrict the
values for type and format
○ except they must be strings
● But some values are "defined"
● Many tools can have unexpected
results when type and format are not
one of these combinations
"properties": {
"level": {
"type": "number",
"format": "integer"
},
API Design Guides
To promote a consistent style for APIs, and also to address the requirements and
constraints of tooling, many companies have developed "design guides" for their APIs:
● Google
○ https://cloud.google.com/apis/design
● Microsoft
○ https://github.com/Microsoft/api-guidelines
○ https://docs.microsoft.com/en-us/azure/architecture/best-practices/api-design
● IBM (in development)
○ http://watson-developer-cloud.github.io/api-guidelines/
○ http://watson-developer-cloud.github.io/api-guidelines/swagger-coding-style
Google API Design Guide
● Collection IDs
○ valid C/C++ identifiers
○ clear and concise English terms
○ avoid overly general terms
○ plural form with lowerCamel case
● Naming conventions
○ Field definitions (property names) must use lower_case_underscore_separated_names.
● Standard field names and types
● Error Model
○ code: integer
○ message: string
○ details: array of object
Microsoft API Design Guide
● All APIs MUST support explicit versioning.
● Services SHOULD provide JSON as the default encoding.
● JSON property names SHOULD be camelCased.
● Error Model
○ error:
■ code: string
■ message: string
■ target: string
■ details: array of object
■ innererror: object
IBM API Design Guide
● Naming conventions
○ Parameter and property names must be lower snake-case
● Data types
○ Parameters and properties should use well-defined data types (type and format)
● Operations
○ Every operation should have a unique operationId
○ List all required parameters before any optional parameters
● Error Model
○ code: integer
○ error: string
○ help: string
API Design -- the devil is in the details
In a perfect world, everyone would agree on the design guidelines for APIs.
Even if everyone agreed on the aesthetics, the disparity of tools and methodologies
used across today's Cloud companies drives each company to establish specific
requirements to match their tooling.
● Approach to versioning
● Naming conventions
● Error models
Validating Compliance
● Trust, but verify.
● There are validators available to flag deviances from OpenAPI.
○ swagger-editor
○ swagger-parser
○ swagger-tools
○ go-swagger
● But the design guidelines we're talking about are generally beyond simple
compliance to the OpenAPI spec.
● And as we've said, each company -- maybe even different divisions within the
same company -- have different API guidelines.
Solution: Linters
Let’s use a solution borrowed from programming languages: linters
● Java: maven checkstyle
● Python: pylint
● Javascript: eslint
● Go: go-tools
The best of these tools are:
● Configurable
○ Let users choose which rules to enforce and which rules to ignore
● Extensible
○ Let users implement their own rules
A Configurable and Extensible Linter for OpenAPI
● All the common linters are written in the same language that they consume.
● So what language should we use for the OpenAPI linter?
● Answer (borrowing from microservices): A Polyglot Linter
○ Linter extensions (plugins) in any language can detect and report violations
Let’s write some linters!
JSON/YAML: in-between language and data structure
Humans can write it easily
but find it tedious and need validators to get it right.
Dynamically-typed languages can read it easily
but can crash on unexpected inputs.
Statically-typed languages can read it easily(?)
but require lots of casting (ugly!) or explicit models.
How to write OpenAPI tooling in static languages
Step 1: Read the OpenAPI spec.
Step 2: Start handwriting data structures and a reader.
Step 3: Notice two things:
1. This is tedious.
2. There’s a JSON schema for OpenAPI.
The OpenAPI JSON Schema
{
"title": "Schema for Swagger 2.0 API.",
"id": "http://swagger.io/v2/schema.json#",
"type": "object",
"required": [
"swagger",
"info",
"paths"
],
"additionalProperties": false,
"patternProperties": {
"^x-": {
"$ref": "#/definitions/vendorExtension"
}
},
"properties": {
"swagger": {
"type": "string",
"enum": [
"2.0"
],
"description": "The version of this document."
},
"info": {
"$ref": "#/definitions/info"
},
"host": {
"type": "string",
"pattern": "^[^{}/ :]+(?::d+)?$",
"description": "The host of the API.'"
},
"definitions": {
"info": {
"type": "object",
"description": "General information”,
"required": [
"version",
"title"
],
"additionalProperties": false,
"patternProperties": {
"^x-": {
"$ref": "#/definitions/vendorExtension"
}
},
"properties": {
"title": {
"type": "string",
"description": "A unique and precise title."
},
"version": {
"type": "string",
"description": "A semantic version number."
},
Can we use JSON Schema to build a data structure factory?
OpenAPI 2.0
JSON Schema
OpenAPI 3.0
JSON Schema
OpenAPI 3.1
JSON Schema
OpenAPI 3.2
JSON Schema
OpenAPI.go
OpenAPI.swift
OpenAPI.rust
OpenAPI.dart
OpenAPI.ex
OpenAPI.kt
Protocol Buffers
a language-neutral, platform-neutral, extensible mechanism
for serializing structured data.
Protocol Buffers?
Serialization mechanism
Interface description language
Methodology
Protocol Buffers, a.k.a Google’s API Factory
search.proto
mail.proto
maps.proto
youtube.proto
search.cpp
search.py
search.java
search.go
......
protoc
protoc-gen-cpp
protoc-gen-py
protoc-gen-java
protoc-gen-go
A data structure factory for OpenAPI
OpenAPI 2.0
JSON Schema
OpenAPI 3.0
JSON Schema
OpenAPI 3.1
JSON Schema
OpenAPIv2.go
OpenAPIv2.swift
OpenAPIv2.rust
OpenAPIv2.dart
OpenAPIv2.ex
OpenAPIv2.kt
protoc-gen-go
protoc-gen-swift
protocgnostic-
generator
OpenAPIv2
.proto
gnostic, the OpenAPI Compiler
gnostic-
generator
OpenAPI
.proto and
support
code
OpenAPI
JSON
schema
protoc +
pluginsOpenAPI
.proto
reusable data structures
and reader for protobuf
OpenAPI descriptions
gnostic apps
and pluginsOpenAPI
description
gnostic parsed and
verified binary
protobuf of the
OpenAPI description
Kubernetes OpenAPI: .json vs .pb
Format Size Deserialization time Download time
(at 80 Mbps)
Json 1653 KB >500 ms 165.3 ms
Proto binary 914 KB 9.3 ms 91.4 ms
Proto binary
compressed
96 KB 13.5 ms 1.3 ms
Inside OpenAPI.proto: it starts with a Document
From OpenAPIv2/OpenAPIv2.proto:
message Document {
string swagger = 1;
Info info = 2;
string host = 3;
string base_path = 4;
repeated string schemes = 5;
// A list of MIME types accepted by the API.
repeated string consumes = 6;
// A list of MIME types the API can produce.
repeated string produces = 7;
Paths paths = 8;
Definitions definitions = 9;
ParameterDefinitions parameters = 10;
ResponseDefinitions responses = 11;
repeated SecurityRequirement security = 12;
SecurityDefinitions security_definitions = 13;
repeated Tag tags = 14;
ExternalDocs external_docs = 15;
repeated NamedAny vendor_extension = 16;
Inside OpenAPI.proto: preserving ordering in maps
From OpenAPIv2/OpenAPIv2.proto:
// One or more JSON representations for parameters
message ParameterDefinitions {
repeated NamedParameter additional_properties = 1;
}
// Automatically-generated message used to represent maps of Parameter as ordered (name,value) pairs.
message NamedParameter {
// Map key
string name = 1;
// Mapped value
Parameter value = 2;
}
message Parameter {
oneof oneof {
BodyParameter body_parameter = 1;
NonBodyParameter non_body_parameter = 2;
}
}
Inside OpenAPI.proto: representing paths
From OpenAPIv2/OpenAPIv2.proto:
// Relative paths to the individual endpoints. They must be relative to the 'basePath'.
message Paths {
repeated NamedAny vendor_extension = 1;
repeated NamedPathItem path = 2;
}
// Automatically-generated message used to represent maps of PathItem as ordered (name,value) pairs.
message NamedPathItem {
// Map key
string name = 1;
// Mapped value
PathItem value = 2;
}
message PathItem {
string _ref = 1;
Operation get = 2;
Operation put = 3;
Operation post = 4;
...
}
gnostic writes a “Request” message to plugins
From plugins/plugin.proto:
// A parameter passed to the plugin from (or through) gnostic.
message Parameter {
// The name of the parameter as specified in the option string
string name = 1;
// The parameter value as specified in the option string
string value = 2;
}
// An encoded Request is written to the plugin's stdin.
message Request {
// filename or URL of the original source document
string source_name = 1;
// Output path specified in the plugin invocation.
string output_path = 2;
// Plugin parameters parsed from the invocation string.
repeated Parameter parameters = 3;
// The version number of gnostic.
Version compiler_version = 4;
// API models
repeated google.protobuf.Any models = 5;
From google/protobuf/any.proto:
message Any {
// A URL/resource name that uniquely
// identifies the type of the serialized
// protocol buffer message.
string type_url = 1;
// Must be a valid serialized protocol
// buffer of the above specified type.
bytes value = 2;
}
plugins write “Response” messages back to gnostic
From plugins/plugin.proto:
// The plugin writes an encoded Response to stdout.
message Response {
// error messages. If non-empty, the plugin failed.
repeated string errors = 1;
// file output, each file will be written by gnostic to an appropriate location.
repeated File files = 2;
// informational messages to be collected and reported by gnostic.
repeated Message messages = 3;
}
// File describes a file generated by a plugin.
message File {
// name of the file
string name = 1;
// data to be written to the file
bytes data = 2;
}
New for linters: message responses
From plugins/plugin.proto:
// Plugins can return messages to be collated and reported by gnostic.
message Message {
enum Level {
UNKNOWN = 0;
INFO = 1;
WARNING = 2;
ERROR = 3;
FATAL = 4;
}
// message severity
Level level = 1;
// a unique message identifier
string code = 2;
// message text
string text = 3;
// an associated key path in an API description
repeated string keys = 4;
}
Linter examples
● Go
○ gnostic-lint-descriptions
○ gnostic-lint-paths
● Node.js
○ gnostic-lint-operations
○ gnostic-lint-responses
● Swift
○ gnostic-lint-responses-swift
Let’s run one.
$ gnostic examples/v2.0/yaml/petstore.yaml --lint-responses
level:ERROR code:"NO_ARRAY_RESPONSES" text:"Arrays MUST NOT be returned as the
top-level structure in a response body." keys:"paths" keys:"/pets" keys:"get"
keys:"responses" keys:"200" keys:"schema"
level:ERROR code:"NO_ARRAY_RESPONSES" text:"Arrays MUST NOT be returned as the
top-level structure in a response body." keys:"paths" keys:"/pets/{petId}"
keys:"get" keys:"responses" keys:"200" keys:"schema"
$ gnostic examples/v2.0/yaml/petstore.yaml --lint-responses --messages-out=.
$ report-messages petstore.messages.pb
ERROR NO_ARRAY_RESPONSES Arrays MUST NOT be returned as the top-level
structure in a response body. [paths /pets get responses 200 schema]
ERROR NO_ARRAY_RESPONSES Arrays MUST NOT be returned as the top-level
structure in a response body. [paths /pets/{petId} get responses 200 schema]
Let’s run ALL of them. (1 / 2)
$ gnostic examples/v2.0/yaml/petstore.yaml 
--lint-responses 
--lint-descriptions 
--lint-paths 
--lint-operations 
--lint-responses-swift 
--messages-out=. 
--time-plugins
Let’s run ALL of them. (2 / 2)
$ report-messages petstore.messages.pb
ERROR NO_ARRAY_RESPONSES Arrays MUST NOT be returned as the top-level structure in a response body. [paths ...
ERROR NO_ARRAY_RESPONSES Arrays MUST NOT be returned as the top-level structure in a response body. [paths ...
WARNING NODESCRIPTION Operation has no description. [paths /pets get]
WARNING NODESCRIPTION Response has no description. [paths /pets get responses 200]
WARNING NODESCRIPTION Response has no description. [paths /pets get responses default]
WARNING NODESCRIPTION Operation has no description. [paths /pets post]
WARNING NODESCRIPTION Response has no description. [paths /pets post responses default]
WARNING NODESCRIPTION Operation has no description. [paths /pets/{petId} get]
WARNING NODESCRIPTION Response has no description. [paths /pets/{petId} get responses 200]
WARNING NODESCRIPTION Response has no description. [paths /pets/{petId} get responses default]
WARNING NODESCRIPTION Definition has no description. [definitions Pet]
WARNING NODESCRIPTION Property has no description. [definitions Pet properties id]
WARNING NODESCRIPTION Property has no description. [definitions Pet properties name]
WARNING NODESCRIPTION Property has no description. [definitions Pet properties tag]
WARNING NODESCRIPTION Definition has no description. [definitions Pets]
WARNING NODESCRIPTION Definition has no description. [definitions Error]
WARNING NODESCRIPTION Property has no description. [definitions Error properties code]
WARNING NODESCRIPTION Property has no description. [definitions Error properties message]
INFO PATH /pets [paths /pets]
INFO PATH /pets/{petId} [paths /pets/{petId}]
ERROR NO_ARRAY_RESPONSES Arrays MUST NOT be returned as the top-level structure in a response body. [paths ...
ERROR NO_ARRAY_RESPONSES Arrays MUST NOT be returned as the top-level structure in a response body. [paths ...
Runtime comparisons
Plugin Language Plugin Run Time
gnostic-lint-operations Node.js 195 ms
gnostic-lint-responses Node.js 221 ms
gnostic-lint-responses-swift Swift 17.8 ms
gnostic-lint-descriptions Go 18.5 ms
gnostic-lint-paths Go 18.6 ms
Where is this going?
● Organization-specific linters, e.g.
○ gnostic-lint-ibm
○ gnostic-lint-google
○ gnostic-lint-supercodegen
● Per-language linter-helper libraries, e.g.
○ Messaging helpers
○ Reference resolution
● More API tools: code generators, API management, smart clients, …
Better API experiences!
https://github.com/googleapis/gnostic

Enforcing API Design Rules for High Quality Code Generation

  • 1.
    Enforcing API Design Rulesfor High-Quality Code Generation Mike Kistler, IBM Tim Burks, Google
  • 2.
  • 3.
    Open API Industry standardformat for describing for REST APIs
  • 5.
    Open API Tooling Originallydesigned for documentation, tools are now available to automate or assist with many common tasks: ● API Authoring ● Validation ● Documentation ● Testing ● Mocking ● Management ● Code Generation ● Validation
  • 6.
    OpenAPI-based Tooling atIBM ● SDK Generation for Watson Developer Cloud ○ Java ○ Node ○ Python ○ Swift ○ .NET ● Benefits ○ Faster time to Market ○ Improved Quality ○ Enforce Compliance ○ Enforce Consistency ○ Increase Adoption https://github.com/watson-developer-cloud
  • 7.
    Some challenges forOpenAPI-based tools ● Some elements of an OAS API definition are "optional" or "flexible" ○ Parameter and property type and format ○ OperationId ○ Response schema ● Some styles of definitions are problematic for tools ○ Inline responses ○ Content-type, accept-type ● Focus implementation effort on main use cases
  • 8.
    Parameter and propertytype and format ● OpenAPI v2 does not restrict the values for type and format ○ except they must be strings ● But some values are "defined" ● Many tools can have unexpected results when type and format are not one of these combinations "properties": { "level": { "type": "number", "format": "integer" },
  • 9.
    API Design Guides Topromote a consistent style for APIs, and also to address the requirements and constraints of tooling, many companies have developed "design guides" for their APIs: ● Google ○ https://cloud.google.com/apis/design ● Microsoft ○ https://github.com/Microsoft/api-guidelines ○ https://docs.microsoft.com/en-us/azure/architecture/best-practices/api-design ● IBM (in development) ○ http://watson-developer-cloud.github.io/api-guidelines/ ○ http://watson-developer-cloud.github.io/api-guidelines/swagger-coding-style
  • 10.
    Google API DesignGuide ● Collection IDs ○ valid C/C++ identifiers ○ clear and concise English terms ○ avoid overly general terms ○ plural form with lowerCamel case ● Naming conventions ○ Field definitions (property names) must use lower_case_underscore_separated_names. ● Standard field names and types ● Error Model ○ code: integer ○ message: string ○ details: array of object
  • 11.
    Microsoft API DesignGuide ● All APIs MUST support explicit versioning. ● Services SHOULD provide JSON as the default encoding. ● JSON property names SHOULD be camelCased. ● Error Model ○ error: ■ code: string ■ message: string ■ target: string ■ details: array of object ■ innererror: object
  • 12.
    IBM API DesignGuide ● Naming conventions ○ Parameter and property names must be lower snake-case ● Data types ○ Parameters and properties should use well-defined data types (type and format) ● Operations ○ Every operation should have a unique operationId ○ List all required parameters before any optional parameters ● Error Model ○ code: integer ○ error: string ○ help: string
  • 13.
    API Design --the devil is in the details In a perfect world, everyone would agree on the design guidelines for APIs. Even if everyone agreed on the aesthetics, the disparity of tools and methodologies used across today's Cloud companies drives each company to establish specific requirements to match their tooling. ● Approach to versioning ● Naming conventions ● Error models
  • 14.
    Validating Compliance ● Trust,but verify. ● There are validators available to flag deviances from OpenAPI. ○ swagger-editor ○ swagger-parser ○ swagger-tools ○ go-swagger ● But the design guidelines we're talking about are generally beyond simple compliance to the OpenAPI spec. ● And as we've said, each company -- maybe even different divisions within the same company -- have different API guidelines.
  • 15.
    Solution: Linters Let’s usea solution borrowed from programming languages: linters ● Java: maven checkstyle ● Python: pylint ● Javascript: eslint ● Go: go-tools The best of these tools are: ● Configurable ○ Let users choose which rules to enforce and which rules to ignore ● Extensible ○ Let users implement their own rules
  • 16.
    A Configurable andExtensible Linter for OpenAPI ● All the common linters are written in the same language that they consume. ● So what language should we use for the OpenAPI linter? ● Answer (borrowing from microservices): A Polyglot Linter ○ Linter extensions (plugins) in any language can detect and report violations
  • 17.
  • 18.
    JSON/YAML: in-between languageand data structure Humans can write it easily but find it tedious and need validators to get it right. Dynamically-typed languages can read it easily but can crash on unexpected inputs. Statically-typed languages can read it easily(?) but require lots of casting (ugly!) or explicit models.
  • 19.
    How to writeOpenAPI tooling in static languages Step 1: Read the OpenAPI spec. Step 2: Start handwriting data structures and a reader. Step 3: Notice two things: 1. This is tedious. 2. There’s a JSON schema for OpenAPI.
  • 20.
    The OpenAPI JSONSchema { "title": "Schema for Swagger 2.0 API.", "id": "http://swagger.io/v2/schema.json#", "type": "object", "required": [ "swagger", "info", "paths" ], "additionalProperties": false, "patternProperties": { "^x-": { "$ref": "#/definitions/vendorExtension" } }, "properties": { "swagger": { "type": "string", "enum": [ "2.0" ], "description": "The version of this document." }, "info": { "$ref": "#/definitions/info" }, "host": { "type": "string", "pattern": "^[^{}/ :]+(?::d+)?$", "description": "The host of the API.'" }, "definitions": { "info": { "type": "object", "description": "General information”, "required": [ "version", "title" ], "additionalProperties": false, "patternProperties": { "^x-": { "$ref": "#/definitions/vendorExtension" } }, "properties": { "title": { "type": "string", "description": "A unique and precise title." }, "version": { "type": "string", "description": "A semantic version number." },
  • 21.
    Can we useJSON Schema to build a data structure factory? OpenAPI 2.0 JSON Schema OpenAPI 3.0 JSON Schema OpenAPI 3.1 JSON Schema OpenAPI 3.2 JSON Schema OpenAPI.go OpenAPI.swift OpenAPI.rust OpenAPI.dart OpenAPI.ex OpenAPI.kt
  • 22.
    Protocol Buffers a language-neutral,platform-neutral, extensible mechanism for serializing structured data.
  • 23.
  • 24.
    Protocol Buffers, a.k.aGoogle’s API Factory search.proto mail.proto maps.proto youtube.proto search.cpp search.py search.java search.go ...... protoc protoc-gen-cpp protoc-gen-py protoc-gen-java protoc-gen-go
  • 25.
    A data structurefactory for OpenAPI OpenAPI 2.0 JSON Schema OpenAPI 3.0 JSON Schema OpenAPI 3.1 JSON Schema OpenAPIv2.go OpenAPIv2.swift OpenAPIv2.rust OpenAPIv2.dart OpenAPIv2.ex OpenAPIv2.kt protoc-gen-go protoc-gen-swift protocgnostic- generator OpenAPIv2 .proto
  • 26.
    gnostic, the OpenAPICompiler gnostic- generator OpenAPI .proto and support code OpenAPI JSON schema protoc + pluginsOpenAPI .proto reusable data structures and reader for protobuf OpenAPI descriptions gnostic apps and pluginsOpenAPI description gnostic parsed and verified binary protobuf of the OpenAPI description
  • 27.
    Kubernetes OpenAPI: .jsonvs .pb Format Size Deserialization time Download time (at 80 Mbps) Json 1653 KB >500 ms 165.3 ms Proto binary 914 KB 9.3 ms 91.4 ms Proto binary compressed 96 KB 13.5 ms 1.3 ms
  • 28.
    Inside OpenAPI.proto: itstarts with a Document From OpenAPIv2/OpenAPIv2.proto: message Document { string swagger = 1; Info info = 2; string host = 3; string base_path = 4; repeated string schemes = 5; // A list of MIME types accepted by the API. repeated string consumes = 6; // A list of MIME types the API can produce. repeated string produces = 7; Paths paths = 8; Definitions definitions = 9; ParameterDefinitions parameters = 10; ResponseDefinitions responses = 11; repeated SecurityRequirement security = 12; SecurityDefinitions security_definitions = 13; repeated Tag tags = 14; ExternalDocs external_docs = 15; repeated NamedAny vendor_extension = 16;
  • 29.
    Inside OpenAPI.proto: preservingordering in maps From OpenAPIv2/OpenAPIv2.proto: // One or more JSON representations for parameters message ParameterDefinitions { repeated NamedParameter additional_properties = 1; } // Automatically-generated message used to represent maps of Parameter as ordered (name,value) pairs. message NamedParameter { // Map key string name = 1; // Mapped value Parameter value = 2; } message Parameter { oneof oneof { BodyParameter body_parameter = 1; NonBodyParameter non_body_parameter = 2; } }
  • 30.
    Inside OpenAPI.proto: representingpaths From OpenAPIv2/OpenAPIv2.proto: // Relative paths to the individual endpoints. They must be relative to the 'basePath'. message Paths { repeated NamedAny vendor_extension = 1; repeated NamedPathItem path = 2; } // Automatically-generated message used to represent maps of PathItem as ordered (name,value) pairs. message NamedPathItem { // Map key string name = 1; // Mapped value PathItem value = 2; } message PathItem { string _ref = 1; Operation get = 2; Operation put = 3; Operation post = 4; ... }
  • 31.
    gnostic writes a“Request” message to plugins From plugins/plugin.proto: // A parameter passed to the plugin from (or through) gnostic. message Parameter { // The name of the parameter as specified in the option string string name = 1; // The parameter value as specified in the option string string value = 2; } // An encoded Request is written to the plugin's stdin. message Request { // filename or URL of the original source document string source_name = 1; // Output path specified in the plugin invocation. string output_path = 2; // Plugin parameters parsed from the invocation string. repeated Parameter parameters = 3; // The version number of gnostic. Version compiler_version = 4; // API models repeated google.protobuf.Any models = 5; From google/protobuf/any.proto: message Any { // A URL/resource name that uniquely // identifies the type of the serialized // protocol buffer message. string type_url = 1; // Must be a valid serialized protocol // buffer of the above specified type. bytes value = 2; }
  • 32.
    plugins write “Response”messages back to gnostic From plugins/plugin.proto: // The plugin writes an encoded Response to stdout. message Response { // error messages. If non-empty, the plugin failed. repeated string errors = 1; // file output, each file will be written by gnostic to an appropriate location. repeated File files = 2; // informational messages to be collected and reported by gnostic. repeated Message messages = 3; } // File describes a file generated by a plugin. message File { // name of the file string name = 1; // data to be written to the file bytes data = 2; }
  • 33.
    New for linters:message responses From plugins/plugin.proto: // Plugins can return messages to be collated and reported by gnostic. message Message { enum Level { UNKNOWN = 0; INFO = 1; WARNING = 2; ERROR = 3; FATAL = 4; } // message severity Level level = 1; // a unique message identifier string code = 2; // message text string text = 3; // an associated key path in an API description repeated string keys = 4; }
  • 34.
    Linter examples ● Go ○gnostic-lint-descriptions ○ gnostic-lint-paths ● Node.js ○ gnostic-lint-operations ○ gnostic-lint-responses ● Swift ○ gnostic-lint-responses-swift
  • 35.
    Let’s run one. $gnostic examples/v2.0/yaml/petstore.yaml --lint-responses level:ERROR code:"NO_ARRAY_RESPONSES" text:"Arrays MUST NOT be returned as the top-level structure in a response body." keys:"paths" keys:"/pets" keys:"get" keys:"responses" keys:"200" keys:"schema" level:ERROR code:"NO_ARRAY_RESPONSES" text:"Arrays MUST NOT be returned as the top-level structure in a response body." keys:"paths" keys:"/pets/{petId}" keys:"get" keys:"responses" keys:"200" keys:"schema" $ gnostic examples/v2.0/yaml/petstore.yaml --lint-responses --messages-out=. $ report-messages petstore.messages.pb ERROR NO_ARRAY_RESPONSES Arrays MUST NOT be returned as the top-level structure in a response body. [paths /pets get responses 200 schema] ERROR NO_ARRAY_RESPONSES Arrays MUST NOT be returned as the top-level structure in a response body. [paths /pets/{petId} get responses 200 schema]
  • 36.
    Let’s run ALLof them. (1 / 2) $ gnostic examples/v2.0/yaml/petstore.yaml --lint-responses --lint-descriptions --lint-paths --lint-operations --lint-responses-swift --messages-out=. --time-plugins
  • 37.
    Let’s run ALLof them. (2 / 2) $ report-messages petstore.messages.pb ERROR NO_ARRAY_RESPONSES Arrays MUST NOT be returned as the top-level structure in a response body. [paths ... ERROR NO_ARRAY_RESPONSES Arrays MUST NOT be returned as the top-level structure in a response body. [paths ... WARNING NODESCRIPTION Operation has no description. [paths /pets get] WARNING NODESCRIPTION Response has no description. [paths /pets get responses 200] WARNING NODESCRIPTION Response has no description. [paths /pets get responses default] WARNING NODESCRIPTION Operation has no description. [paths /pets post] WARNING NODESCRIPTION Response has no description. [paths /pets post responses default] WARNING NODESCRIPTION Operation has no description. [paths /pets/{petId} get] WARNING NODESCRIPTION Response has no description. [paths /pets/{petId} get responses 200] WARNING NODESCRIPTION Response has no description. [paths /pets/{petId} get responses default] WARNING NODESCRIPTION Definition has no description. [definitions Pet] WARNING NODESCRIPTION Property has no description. [definitions Pet properties id] WARNING NODESCRIPTION Property has no description. [definitions Pet properties name] WARNING NODESCRIPTION Property has no description. [definitions Pet properties tag] WARNING NODESCRIPTION Definition has no description. [definitions Pets] WARNING NODESCRIPTION Definition has no description. [definitions Error] WARNING NODESCRIPTION Property has no description. [definitions Error properties code] WARNING NODESCRIPTION Property has no description. [definitions Error properties message] INFO PATH /pets [paths /pets] INFO PATH /pets/{petId} [paths /pets/{petId}] ERROR NO_ARRAY_RESPONSES Arrays MUST NOT be returned as the top-level structure in a response body. [paths ... ERROR NO_ARRAY_RESPONSES Arrays MUST NOT be returned as the top-level structure in a response body. [paths ...
  • 38.
    Runtime comparisons Plugin LanguagePlugin Run Time gnostic-lint-operations Node.js 195 ms gnostic-lint-responses Node.js 221 ms gnostic-lint-responses-swift Swift 17.8 ms gnostic-lint-descriptions Go 18.5 ms gnostic-lint-paths Go 18.6 ms
  • 39.
    Where is thisgoing? ● Organization-specific linters, e.g. ○ gnostic-lint-ibm ○ gnostic-lint-google ○ gnostic-lint-supercodegen ● Per-language linter-helper libraries, e.g. ○ Messaging helpers ○ Reference resolution ● More API tools: code generators, API management, smart clients, … Better API experiences!
  • 40.