diff --git a/packages/schema/exported-schema.json b/packages/schema/exported-schema.json index f971ed8cb..2ade92ebc 100644 --- a/packages/schema/exported-schema.json +++ b/packages/schema/exported-schema.json @@ -1063,6 +1063,83 @@ }, "additionalProperties": false }, + "BasicHookToPollOperationSchema": { + "id": "/BasicHookToPollOperationSchema", + "description": "Represents the inbound mechanics of hook to poll style triggers. Defers to list for fields.", + "type": "object", + "required": ["performList", "performSubscribe", "performUnsubscribe"], + "properties": { + "type": { + "description": "Must be explicitly set to `\"hook_to_poll\"`.", + "type": "string", + "enum": ["hook_to_poll"], + "required": { + "type": "replace", + "value": "**yes** (with exceptions, see description)" + } + }, + "performList": { + "description": "Similar a polling trigger, but checks for new data when a webhook is received, instead of every few minutes", + "oneOf": [ + { + "$ref": "/RequestSchema" + }, + { + "$ref": "/FunctionSchema" + } + ] + }, + "canPaginate": { + "description": "Does this endpoint support pagination via temporary cursor storage?", + "type": "boolean" + }, + "performSubscribe": { + "description": "Takes a URL and any necessary data from the user and subscribes. ", + "oneOf": [ + { + "$ref": "/RequestSchema" + }, + { + "$ref": "/FunctionSchema" + } + ] + }, + "performUnsubscribe": { + "description": "Takes a URL and data from a previous subscribe call and unsubscribes. ", + "oneOf": [ + { + "$ref": "/RequestSchema" + }, + { + "$ref": "/FunctionSchema" + } + ] + }, + "inputFields": { + "description": "What should the form a user sees and configures look like?", + "$ref": "/DynamicFieldsSchema" + }, + "outputFields": { + "description": "What fields of data will this return? Will use resource outputFields if missing, will also use sample if available.", + "$ref": "/DynamicFieldsSchema" + }, + "sample": { + "description": "What does a sample of data look like? Will use resource sample if missing. Requirement waived if `display.hidden` is true or if this belongs to a resource that has a top-level sample", + "type": "object", + "minProperties": 1, + "docAnnotation": { + "required": { + "type": "replace", + "value": "**yes** (with exceptions, see description)" + } + } + } + }, + "additionalProperties": false, + "docAnnotation": { + "hide": true + } + }, "TriggerSchema": { "id": "/TriggerSchema", "description": "How will Zapier get notified of new objects?", @@ -1091,6 +1168,9 @@ }, { "$ref": "/BasicHookOperationSchema" + }, + { + "$ref": "/BasicHookToPollOperationSchema" } ] } diff --git a/packages/schema/lib/schemas/BasicHookToPollOperationSchema.js b/packages/schema/lib/schemas/BasicHookToPollOperationSchema.js new file mode 100644 index 000000000..1987f4d5a --- /dev/null +++ b/packages/schema/lib/schemas/BasicHookToPollOperationSchema.js @@ -0,0 +1,93 @@ +'use strict'; + +const makeSchema = require('../utils/makeSchema'); +const { SKIP_KEY } = require('../constants'); + +const BasicOperationSchema = require('./BasicOperationSchema'); +const FunctionSchema = require('./FunctionSchema'); +const RequestSchema = require('./RequestSchema'); + +// TODO: would be nice to deep merge these instead +// or maybe use allOf which is built into json-schema +const BasicHookToPollOperationSchema = JSON.parse( + JSON.stringify(BasicOperationSchema.schema) +); + +BasicHookToPollOperationSchema.id = '/BasicHookToPollOperationSchema'; + +BasicHookToPollOperationSchema.description = + 'Represents the inbound mechanics of hook to poll style triggers. Defers to list for fields.'; + +BasicHookToPollOperationSchema.docAnnotation = { + hide: true, +}; + +BasicHookToPollOperationSchema.required = [ + 'performList', + 'performSubscribe', + 'performUnsubscribe', +]; + +BasicHookToPollOperationSchema.properties = { + type: { + description: 'Must be explicitly set to `"hook_to_poll"`.', + type: 'string', + enum: ['hook_to_poll'], + required: { + type: 'replace', + value: '**yes** (with exceptions, see description)', + }, + }, + performList: { + description: + 'Similar a polling trigger, but checks for new data when a webhook is received, instead of every few minutes', + oneOf: [{ $ref: RequestSchema.id }, { $ref: FunctionSchema.id }], + }, + canPaginate: { + description: + 'Does this endpoint support pagination via temporary cursor storage?', + type: 'boolean', + }, + performSubscribe: { + description: + 'Takes a URL and any necessary data from the user and subscribes. ', + oneOf: [{ $ref: RequestSchema.id }, { $ref: FunctionSchema.id }], + }, + performUnsubscribe: { + description: + 'Takes a URL and data from a previous subscribe call and unsubscribes. ', + oneOf: [{ $ref: RequestSchema.id }, { $ref: FunctionSchema.id }], + }, + inputFields: BasicHookToPollOperationSchema.properties.inputFields, + outputFields: BasicHookToPollOperationSchema.properties.outputFields, + sample: BasicHookToPollOperationSchema.properties.sample, +}; + +BasicHookToPollOperationSchema.examples = [ + { + type: 'hook_to_poll', + performList: { require: 'some/path/to/file2.js' }, + performSubscribe: { require: 'some/path/to/file3.js' }, + performUnsubscribe: { require: 'some/path/to/file4.js' }, + sample: { id: 42, name: 'Hooli' }, + }, +]; + +BasicHookToPollOperationSchema.antiExamples = [ + { + [SKIP_KEY]: true, // Cannot validate that sample is only required if display isn't true + example: { + type: 'hook_to_poll', + performList: { require: 'some/path/to/file2.js' }, + performSubscribe: { require: 'some/path/to/file3.js' }, + performUnsubscribe: { require: 'some/path/to/file4.js' }, + }, + reason: + 'Missing required key: sample. Note - This is only invalid if `display` is not explicitly set to true', + }, +]; + +module.exports = makeSchema( + BasicHookToPollOperationSchema, + BasicOperationSchema.dependencies +); diff --git a/packages/schema/lib/schemas/TriggerSchema.js b/packages/schema/lib/schemas/TriggerSchema.js index f2d5028b6..f8e8e34c5 100644 --- a/packages/schema/lib/schemas/TriggerSchema.js +++ b/packages/schema/lib/schemas/TriggerSchema.js @@ -4,6 +4,7 @@ const makeSchema = require('../utils/makeSchema'); const BasicDisplaySchema = require('./BasicDisplaySchema'); const BasicHookOperationSchema = require('./BasicHookOperationSchema'); +const BasicHookToPollOperationSchema = require('./BasicHookToPollOperationSchema'); const BasicPollingOperationSchema = require('./BasicPollingOperationSchema'); const KeySchema = require('./KeySchema'); @@ -34,6 +35,7 @@ module.exports = makeSchema( anyOf: [ { $ref: BasicPollingOperationSchema.id }, { $ref: BasicHookOperationSchema.id }, + { $ref: BasicHookToPollOperationSchema.id }, ], }, }, @@ -89,5 +91,6 @@ module.exports = makeSchema( BasicDisplaySchema, BasicPollingOperationSchema, BasicHookOperationSchema, + BasicHookToPollOperationSchema, ] ); diff --git a/packages/schema/lib/utils/buildDocs.js b/packages/schema/lib/utils/buildDocs.js index e18dad8c3..31c423e0d 100644 --- a/packages/schema/lib/utils/buildDocs.js +++ b/packages/schema/lib/utils/buildDocs.js @@ -11,6 +11,7 @@ const links = require('./links'); const NO_DESCRIPTION = '_No description given._'; const COMBOS = ['anyOf', 'allOf', 'oneOf']; const { SKIP_KEY } = require('../constants'); +const hiddenRefs = []; const walkSchemas = (InitSchema, callback) => { const recurse = (Schema, parents) => { @@ -27,7 +28,11 @@ const walkSchemas = (InitSchema, callback) => { const collectSchemas = (InitSchema) => { const schemas = {}; walkSchemas(InitSchema, (Schema) => { - schemas[Schema.id] = Schema; + if (!_.get(Schema, 'schema.docAnnotation.hide')) { + schemas[Schema.id] = Schema; + } else { + hiddenRefs.push(Schema.id); + } }); return schemas; }; @@ -64,12 +69,18 @@ const typeOrLink = (schema) => { return `${quoteOrNa(schema.type)}[${typeOrLink(schema.items)}]`; } if (schema.$ref) { - return `[${schema.$ref}](${links.anchor(schema.$ref)})`; + if (!hiddenRefs.includes(schema.$ref)) { + return `[${schema.$ref}](${links.anchor(schema.$ref)})`; + } + return; } for (let i = 0; i < COMBOS.length; i++) { const key = COMBOS[i]; if (schema[key] && schema[key].length) { - return `${key}(${schema[key].map(typeOrLink).join(', ')})`; + return `${key}(${schema[key] + .map(typeOrLink) + .filter(Boolean) + .join(', ')})`; } } if (schema.enum && schema.enum.length) { diff --git a/packages/schema/test/index.js b/packages/schema/test/index.js index 9081e4bab..29a03d6b0 100644 --- a/packages/schema/test/index.js +++ b/packages/schema/test/index.js @@ -9,7 +9,7 @@ const appDefinition = require('../examples/definition.json'); const copy = (o) => JSON.parse(JSON.stringify(o)); -const NUM_SCHEMAS = 50; // changes regularly as we expand +const NUM_SCHEMAS = 51; // changes regularly as we expand describe('app', () => { describe('validation', () => {