diff --git a/content/apps/creating-github-apps/guides/creating-ci-tests-with-the-checks-api.md b/content/apps/creating-github-apps/guides/creating-ci-tests-with-the-checks-api.md index 9eaace4071..57b2cb6536 100644 --- a/content/apps/creating-github-apps/guides/creating-ci-tests-with-the-checks-api.md +++ b/content/apps/creating-github-apps/guides/creating-ci-tests-with-the-checks-api.md @@ -235,7 +235,7 @@ The code above calls the "[AUTOTITLE](/rest/checks#update-a-check-run)" API endp Here's what this code is doing. First, it updates the check run's status to `in_progress` and implicitly sets the `started_at` time to the current time. In [Part 2](#part-2-creating-the-octo-rubocop-ci-test) of this quickstart, you'll add code that kicks off a real CI test under `***** RUN A CI TEST *****`. For now, you'll leave that section as a placeholder, so the code that follows it will just simulate that the CI process succeeds and all tests pass. Finally, the code updates the status of the check run again to `completed`. -You'll notice in the "[AUTOTITLE](/rest/checks#update-a-check-run)" docs that when you provide a status of `completed`, the `conclusion` and `completed_at` parameters are required. The `conclusion` summarizes the outcome of a check run and can be `success`, `failure`, `neutral`, `cancelled`, `timed_out`, or `action_required`. You'll set the conclusion to `success`, the `completed_at` time to the current time, and the status to `completed`. +You'll notice in the "[AUTOTITLE](/rest/checks#update-a-check-run)" docs that when you provide a status of `completed`, the `conclusion` and `completed_at` parameters are required. The `conclusion` summarizes the outcome of a check run and can be `success`, `failure`, `neutral`, `cancelled`, `timed_out`, `skipped`, or `action_required`. You'll set the conclusion to `success`, the `completed_at` time to the current time, and the status to `completed`. You could also provide more details about what your check is doing, but you'll get to that in the next section. Let's test this code again by re-running `template_server.rb`: diff --git a/content/rest/guides/using-the-rest-api-to-interact-with-checks.md b/content/rest/guides/using-the-rest-api-to-interact-with-checks.md index 4e05309baf..5398bec212 100644 --- a/content/rest/guides/using-the-rest-api-to-interact-with-checks.md +++ b/content/rest/guides/using-the-rest-api-to-interact-with-checks.md @@ -23,14 +23,17 @@ For an example of how to use the REST API with a {% data variables.product.prodn When someone pushes code to a repository, GitHub creates a check suite for the last commit. A check suite is a collection of the [check runs](/rest/checks#check-runs) created by a single GitHub App for a specific commit. Check suites summarize the status and conclusion of the check runs that a suite includes. -The `status` can be `queued`, `in_progress`, or `complete`. +The `status` can be `queued`, `in_progress`, or `completed`. -If the status is `complete`, the conclusion can be any of the following: +If the status is `completed`, the conclusion can be any of the following: - `action_required` - `cancelled` - `timed_out` - `failure` - `neutral` +- `skipped` +- `stale` +- `startup_failure` - `success` The check suite reports the highest priority check run `conclusion` in the check suite's `conclusion`. For example, if three check runs have conclusions of `timed_out`, `success`, and `neutral` the check suite conclusion will be `timed_out`. @@ -49,7 +52,7 @@ To use the endpoints to manage check suites, the {% data variables.product.prodn A check run is an individual test that is part of a check suite. Each run includes a status and conclusion. -The status can be `queued`, `in_progress`, or `complete`. +The status can be `queued`, `in_progress`, or `completed`. If the status is `completed`, the conclusion can be any of the following: - `action_required` @@ -57,6 +60,7 @@ If the status is `completed`, the conclusion can be any of the following: - `timed_out` - `failure` - `neutral` +- `skipped` - `success` If a check run is in an incomplete state for more than 14 days, then the check run's `conclusion` becomes `stale` and appears on {% data variables.product.prodname_dotcom %} as stale with {% octicon "issue-reopened" aria-label="The issue-reopened icon" %}. Only {% data variables.product.prodname_dotcom %} can mark check runs as `stale`. For more information about possible conclusions of a check run, see the [`conclusion` parameter](/rest/checks#create-a-check-run--parameters). diff --git a/package-lock.json b/package-lock.json index f79ce3d394..a61718c39f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -80,7 +80,6 @@ "remark-parse": "^10.0.1", "remark-parse-no-trim": "^8.0.4", "remark-rehype": "^10.1.0", - "revalidator": "^0.3.1", "rss-parser": "^3.12.0", "scroll-anchoring": "^0.1.0", "semver": "^7.3.7", @@ -17863,13 +17862,6 @@ "node": ">=0.10.0" } }, - "node_modules/revalidator": { - "version": "0.3.1", - "license": "Apache 2.0", - "engines": { - "node": ">= 0.8.0" - } - }, "node_modules/rfdc": { "version": "1.3.0", "dev": true, @@ -32425,9 +32417,6 @@ "version": "1.0.4", "dev": true }, - "revalidator": { - "version": "0.3.1" - }, "rfdc": { "version": "1.3.0", "dev": true diff --git a/package.json b/package.json index 4b8ff68a71..773768ff0a 100644 --- a/package.json +++ b/package.json @@ -82,7 +82,6 @@ "remark-parse": "^10.0.1", "remark-parse-no-trim": "^8.0.4", "remark-rehype": "^10.1.0", - "revalidator": "^0.3.1", "rss-parser": "^3.12.0", "scroll-anchoring": "^0.1.0", "semver": "^7.3.7", diff --git a/src/graphql/lib/validator.js b/src/graphql/lib/validator.js index ff4b5e1ce4..af1ca18b74 100644 --- a/src/graphql/lib/validator.js +++ b/src/graphql/lib/validator.js @@ -3,67 +3,66 @@ // PREVIEWS export const previewsValidator = { + type: 'object', + required: [ + 'title', + 'description', + 'toggled_by', + 'toggled_on', + 'owning_teams', + 'accept_header', + 'href', + ], properties: { title: { type: 'string', - required: true, }, description: { type: 'string', - required: true, }, toggled_by: { type: 'string', - required: true, }, toggled_on: { type: 'array', - required: true, }, owning_teams: { type: 'array', - required: true, }, accept_header: { type: 'string', - required: true, }, href: { type: 'string', - required: true, }, }, } // UPCOMING CHANGES export const upcomingChangesValidator = { + type: 'object', + required: ['location', 'description', 'reason', 'date', 'criticality', 'owner'], properties: { location: { type: 'string', - required: true, }, description: { type: 'string', - required: true, }, reason: { type: 'string', - required: true, }, date: { type: 'string', - required: true, - pattern: /^\d{4}-\d{2}-\d{2}$/, + pattern: '^\\d{4}-\\d{2}-\\d{2}$', }, criticality: { type: 'string', - required: true, pattern: '(breaking|dangerous)', }, owner: { type: 'string', - required: true, - pattern: /^[\S]*$/, + pattern: '^[\\S]*$', }, }, } @@ -74,35 +73,27 @@ const coreProps = { properties: { name: { type: 'string', - required: true, }, type: { type: 'string', - required: true, }, kind: { type: 'string', - required: true, }, id: { type: 'string', - required: true, }, href: { type: 'string', - required: true, }, description: { type: 'string', - required: true, }, isDeprecated: { type: 'boolean', - required: false, }, preview: { type: 'object', - required: false, properties: previewsValidator.properties, }, }, @@ -113,14 +104,15 @@ const corePropsPlusArgs = dup(coreProps) corePropsPlusArgs.properties.args = { type: 'array', - required: false, - properties: coreProps.properties, + items: { + type: 'object', + properties: coreProps.properties, + }, } // the args object can have defaultValue prop -corePropsPlusArgs.properties.args.properties.defaultValue = { +corePropsPlusArgs.properties.args.items.properties.defaultValue = { type: 'boolean', - required: false, } const corePropsNoType = dup(coreProps) @@ -132,45 +124,60 @@ delete corePropsNoDescription.properties.description // QUERIES const queries = dup(corePropsPlusArgs) +queries.type = 'object' +queries.required = ['name', 'type', 'kind', 'id', 'href', 'description'] + // MUTATIONS const mutations = dup(corePropsNoType) +mutations.type = 'object' +mutations.required = ['name', 'kind', 'id', 'href', 'description', 'inputFields', 'returnFields'] + mutations.properties.inputFields = { type: 'array', - required: true, - properties: corePropsNoDescription.properties, + items: { + type: 'object', + properties: corePropsNoDescription.properties, + }, } mutations.properties.returnFields = { type: 'array', - required: true, - properties: coreProps.properties, + items: { + type: 'object', + properties: coreProps.properties, + }, } // OBJECTS const objects = dup(corePropsNoType) +objects.type = 'object' +objects.required = ['name', 'kind', 'id', 'href', 'description', 'fields'] + objects.properties.fields = { type: 'array', - required: true, - properties: corePropsPlusArgs.properties, + items: { + type: 'object', + properties: corePropsPlusArgs.properties, + }, } objects.properties.implements = { type: 'array', - required: false, - properties: { - name: { - type: 'string', - required: true, - }, - id: { - type: 'string', - required: true, - }, - href: { - type: 'string', - required: true, + items: { + type: 'object', + required: ['name', 'id', 'href'], + properties: { + name: { + type: 'string', + }, + id: { + type: 'string', + }, + href: { + type: 'string', + }, }, }, } @@ -178,26 +185,35 @@ objects.properties.implements = { // INTERFACES const interfaces = dup(corePropsNoType) +interfaces.type = 'object' +interfaces.required = ['name', 'kind', 'id', 'href', 'description', 'fields'] + interfaces.properties.fields = { type: 'array', - required: true, - properties: corePropsPlusArgs.properties, + items: { + type: 'object', + properties: corePropsPlusArgs.properties, + }, } // ENUMS const enums = dup(corePropsNoType) +enums.type = 'object' +enums.required = ['name', 'kind', 'id', 'href', 'description', 'values'] + enums.properties.values = { type: 'array', - required: true, - properties: { - name: { - type: 'string', - required: true, - }, - description: { - type: 'string', - required: true, + items: { + type: 'object', + required: ['name', 'description'], + properties: { + name: { + type: 'string', + }, + description: { + type: 'string', + }, }, }, } @@ -205,21 +221,24 @@ enums.properties.values = { // UNIONS const unions = dup(corePropsNoType) +unions.type = 'object' +unions.required = ['name', 'kind', 'id', 'href', 'description', 'possibleTypes'] + unions.properties.possibleTypes = { type: 'array', - required: true, - properties: { - name: { - type: 'string', - required: true, - }, - id: { - type: 'string', - required: true, - }, - href: { - type: 'string', - required: true, + items: { + type: 'object', + required: ['name', 'id', 'href'], + properties: { + name: { + type: 'string', + }, + id: { + type: 'string', + }, + href: { + type: 'string', + }, }, }, } @@ -227,15 +246,22 @@ unions.properties.possibleTypes = { // INPUT OBJECTS const inputObjects = dup(corePropsNoType) +inputObjects.type = 'object' +inputObjects.required = ['name', 'kind', 'id', 'href', 'description', 'inputFields'] + inputObjects.properties.inputFields = { type: 'array', - required: true, - properties: coreProps.properties, + items: { + type: 'object', + properties: coreProps.properties, + }, } // SCALARS const scalars = dup(corePropsNoType) -scalars.properties.kind.required = false + +scalars.type = 'object' +scalars.required = ['name', 'id', 'href', 'description'] function dup(obj) { return JSON.parse(JSON.stringify(obj)) diff --git a/tests/content/graphql.js b/tests/content/graphql.js index 2cf168a379..d8ed41fafd 100644 --- a/tests/content/graphql.js +++ b/tests/content/graphql.js @@ -1,4 +1,5 @@ import { jest } from '@jest/globals' +import Ajv from 'ajv' import readJsonFile from '../../lib/read-json-file.js' import { @@ -6,7 +7,7 @@ import { previewsValidator, upcomingChangesValidator, } from '../../src/graphql/lib/validator.js' -import revalidator from 'revalidator' +import { formatAjvErrors } from '../helpers/schemas.js' import { allVersions } from '../../lib/all-versions.js' import { GRAPHQL_DATA_DIR } from '../../src/graphql/lib/index.js' @@ -14,6 +15,16 @@ const allVersionValues = Object.values(allVersions) const graphqlVersions = allVersionValues.map((v) => v.miscVersionName) const graphqlTypes = readJsonFile('./src/graphql/lib/types.json').map((t) => t.kind) +const ajv = new Ajv({ allErrors: true, allowUnionTypes: true }) +const previewsValidate = ajv.compile(previewsValidator) +const upcomingChangesValidate = ajv.compile(upcomingChangesValidator) +// setup ajv validator functions for each graphql type (e.g. queries, mutations, +// etc.) +const schemaValidatorFunctions = {} +graphqlTypes.forEach((type) => { + schemaValidatorFunctions[type] = ajv.compile(schemaValidator[type]) +}) + describe('graphql json files', () => { jest.setTimeout(3 * 60 * 1000) @@ -31,9 +42,16 @@ describe('graphql json files', () => { if (typeObjsTested.has(key)) return typeObjsTested.add(key) - const { valid, errors } = revalidator.validate(typeObj, schemaValidator[type]) - const errorMessage = JSON.stringify(errors, null, 2) - expect(valid, errorMessage).toBe(true) + const valid = schemaValidatorFunctions[type](typeObj) + let errors + + if (!valid) { + errors = `kind: ${typeObj.kind}, name: ${typeObj.name}: ${formatAjvErrors( + schemaValidatorFunctions[type].errors + )}` + } + + expect(valid, errors).toBe(true) }) }) }) @@ -43,9 +61,14 @@ describe('graphql json files', () => { graphqlVersions.forEach((version) => { const previews = readJsonFile(`${GRAPHQL_DATA_DIR}/${version}/previews.json`) previews.forEach((preview) => { - const { valid, errors } = revalidator.validate(preview, previewsValidator) - const errorMessage = JSON.stringify(errors, null, 2) - expect(valid, errorMessage).toBe(true) + const valid = previewsValidate(preview) + let errors + + if (!valid) { + errors = formatAjvErrors(previewsValidate.errors) + } + + expect(valid, errors).toBe(true) }) }) }) @@ -56,9 +79,14 @@ describe('graphql json files', () => { for (const changes of Object.values(upcomingChanges)) { // each object value is an array of changes changes.forEach((changeObj) => { - const { valid, errors } = revalidator.validate(changeObj, upcomingChangesValidator) - const errorMessage = JSON.stringify(errors, null, 2) - expect(valid, errorMessage).toBe(true) + const valid = upcomingChangesValidate(changeObj) + let errors + + if (!valid) { + errors = formatAjvErrors(upcomingChangesValidate.errors) + } + + expect(valid, errors).toBe(true) }) } })