diff --git a/linter/testcases/language-code/expected-output.txt b/linter/testcases/language-code/expected-output.txt new file mode 100644 index 0000000..fcbcea2 --- /dev/null +++ b/linter/testcases/language-code/expected-output.txt @@ -0,0 +1,17 @@ + +/testcases/language-code/openapi.json + 92:52 error nlgov:specify-format-for-language-code Any field that represents a language must set 'format' to 'language' paths./resources-with-language-incorrect.get.responses[200].content.application/json.schema.properties.language + 95:65 error nlgov:specify-format-for-language-code Any field that represents a language must set 'format' to 'language' paths./resources-with-language-incorrect.get.responses[200].content.application/json.schema.properties.thoroughfare_language + 98:70 error nlgov:specify-format-for-language-code Any field that represents a language must set 'format' to 'language' paths./resources-with-language-incorrect.get.responses[200].content.application/json.schema.properties.railName_localNameLanguage + 101:48 error nlgov:specify-format-for-language-code Any field that represents a language must set 'format' to 'language' paths./resources-with-language-incorrect.get.responses[200].content.application/json.schema.properties.taal + 104:59 error nlgov:specify-format-for-language-code Any field that represents a language must set 'format' to 'language' paths./resources-with-language-incorrect.get.responses[200].content.application/json.schema.properties.organisatieTaal + 107:60 error nlgov:specify-format-for-language-code Any field that represents a language must set 'format' to 'language' paths./resources-with-language-incorrect.get.responses[200].content.application/json.schema.properties.organisatie_taal + 127:55 error nlgov:use-language-code Field represents a language and therefore must set 'format' to 'language' paths./resources-with-language-incorrect.get.responses[200].content.application/json+verkeerd-format.schema.properties.language.format + 131:55 error nlgov:use-language-code Field represents a language and therefore must set 'format' to 'language' paths./resources-with-language-incorrect.get.responses[200].content.application/json+verkeerd-format.schema.properties.thoroughfare_language.format + 135:55 error nlgov:use-language-code Field represents a language and therefore must set 'format' to 'language' paths./resources-with-language-incorrect.get.responses[200].content.application/json+verkeerd-format.schema.properties.railName_localNameLanguage.format + 139:55 error nlgov:use-language-code Field represents a language and therefore must set 'format' to 'language' paths./resources-with-language-incorrect.get.responses[200].content.application/json+verkeerd-format.schema.properties.taal.format + 143:55 error nlgov:use-language-code Field represents a language and therefore must set 'format' to 'language' paths./resources-with-language-incorrect.get.responses[200].content.application/json+verkeerd-format.schema.properties.organisatieTaal.format + 147:55 error nlgov:use-language-code Field represents a language and therefore must set 'format' to 'language' paths./resources-with-language-incorrect.get.responses[200].content.application/json+verkeerd-format.schema.properties.organisatie_taal.format + 236:27 error nlgov:use-language-code Field represents a language and therefore must set 'format' to 'language' components.schemas.LanguageIncorrect.format + +✖ 13 problems (13 errors, 0 warnings, 0 infos, 0 hints) diff --git a/linter/testcases/language-code/openapi.json b/linter/testcases/language-code/openapi.json new file mode 100644 index 0000000..38720e7 --- /dev/null +++ b/linter/testcases/language-code/openapi.json @@ -0,0 +1,256 @@ +{ + "openapi": "3.0.3", + "info": { + "title": "Baseline", + "description": "Deze OpenAPI specification bevat het minimale om aan alle regels te voldoen.", + "contact": { + "name": "Beheerder", + "url": "https://www.example.com", + "email": "mail@example.com" + }, + "version": "1.0.0" + }, + "servers": [ + { + "url": "https://example.com/api/v1" + } + ], + "security": [ + { + "default": [] + } + ], + "tags": [ + { + "name": "openapi" + }, + { + "name": "language" + } + ], + "paths": { + "/openapi.json": { + "get": { + "tags": [ + "openapi" + ], + "description": "OpenAPI document", + "operationId": "getOpenapiJSON", + "parameters": [], + "responses": { + "200": { + "description": "OK", + "headers": { + "API-Version": { + "description": "De huidige versie van de applicatie", + "style": "simple", + "schema": { + "type": "string" + } + }, + "access-control-allow-origin": { + "description": "Alle origins mogen bij deze resource", + "schema": { + "type": "string" + } + } + } + } + }, + "security": [ + { + "default": [] + } + ] + } + }, + "/resources-with-language-incorrect": { + "get": { + "tags": [ + "language" + ], + "description": "Resources with language incorrect", + "operationId": "getLanguageIncorrect", + "parameters": [], + "responses": { + "200": { + "description": "OK", + "headers": { + "API-Version": { + "description": "De huidige versie van de applicatie", + "style": "simple", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "language": { + "type": "string" + }, + "thoroughfare_language": { + "type": "string" + }, + "railName_localNameLanguage": { + "type": "string" + }, + "taal": { + "type": "string" + }, + "organisatieTaal": { + "type": "string" + }, + "organisatie_taal": { + "type": "string" + }, + "meerdereTaal": { + "type": "string", + "allOf": [ + { + "$ref": "#/components/schemas/LanguageIncorrect" + } + ] + } + } + } + }, + "application/json+verkeerd-format": { + "schema": { + "type": "object", + "properties": { + "language": { + "type": "string", + "format": "byte" + }, + "thoroughfare_language": { + "type": "string", + "format": "byte" + }, + "railName_localNameLanguage": { + "type": "string", + "format": "byte" + }, + "taal": { + "type": "string", + "format": "byte" + }, + "organisatieTaal": { + "type": "string", + "format": "byte" + }, + "organisatie_taal": { + "type": "string", + "format": "byte" + } + } + } + } + } + } + }, + "security": [ + { + "default": [] + } + ] + } + }, + "/resources-with-language-correct": { + "get": { + "tags": [ + "language" + ], + "description": "Resources with language correct", + "operationId": "getLanguagesCorrect", + "parameters": [], + "responses": { + "200": { + "description": "OK", + "headers": { + "API-Version": { + "description": "De huidige versie van de applicatie", + "style": "simple", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "language": { + "type": "string", + "format": "language" + }, + "thoroughfare_language": { + "type": "string", + "format": "language" + }, + "railName_localNameLanguage": { + "type": "string", + "format": "language" + }, + "taal": { + "type": "string", + "format": "language" + }, + "organisatieTaal": { + "type": "string", + "format": "language" + }, + "organisatie_taal": { + "type": "string", + "format": "language" + }, + "meerdereTaal": { + "type": "string", + "allOf": [ + { + "$ref": "#/components/schemas/LanguageCorrect" + } + ] + } + } + } + } + } + } + }, + "security": [ + { + "default": [] + } + ] + } + } + }, + "components": { + "schemas": { + "LanguageIncorrect": { + "format": "byte", + "type": "string" + }, + "LanguageCorrect": { + "format": "language", + "type": "string" + } + }, + "securitySchemes": { + "default": { + "type": "oauth2", + "flows": { + "implicit": { + "authorizationUrl": "https://test.com", + "scopes": {} + } + } + } + } + } +} \ No newline at end of file diff --git a/media/linter.yaml b/media/linter.yaml index 679a5aa..03d166a 100644 --- a/media/linter.yaml +++ b/media/linter.yaml @@ -282,6 +282,39 @@ rules: functionOptions: notMatch: "/^date-time$/" + #/core/lang-code + nlgov:specify-format-for-language-code: + severity: error + given: + - $..properties[taal,lang,language] + - $..properties[?(@property && @property.match(/((\w+T)|(_[tT]))aal/))] + - $..properties[?(@property && @property.match(/((\w+L)|(_[lL]))ang(uage)?/))] + message: "Any field that represents a language must set 'format' to 'language'" + then: + function: schema + functionOptions: + schema: + anyOf: + - required: ["format"] + - properties: + allOf: + type: array + items: + required: ["format"] + required: ["allOf"] + + nlgov:use-language-code: + severity: error + given: + - $..properties[taal,lang,language]..format + - $..properties[?(@property && @property.match(/((\w+T)|(_[tT]))aal/))]..format + - $..properties[?(@property && @property.match(/((\w+L)|(_[lL]))ang(uage)?/))]..format + message: "Field represents a language and therefore must set 'format' to 'language'" + then: + function: pattern + functionOptions: + match: "/^language$/" + nlgov:semver: severity: error message: "Version {{value}} is not in semver format." diff --git a/sections/designRules.md b/sections/designRules.md index abddf71..76fab69 100644 --- a/sections/designRules.md +++ b/sections/designRules.md @@ -214,6 +214,34 @@ https://api.example.org/v1/vergunningen/d285e05c-6b01-45c3-92d8-5e19a946b66f +
Use standard language codes and field names for language content
+A resource containing language content MUST follow BCP 47 [[RFC4647]] [[RFC5646]]. + The Primary Language Subtag MUST be lowercase. + Languages MUST have a Region subtag in uppercase. +
[[?ISO3166-1]] concerns identifiers of countries and MUST NOT be used to denote languages, since countries and languages are not equivalent. +
Following [[RFC4647]] a language code in [[?ISO-639-1]] format matches a language tag in [[RFC5646]] format regardless of language subtag. + + +