/
FieldSchema.js
184 lines (177 loc) · 5.9 KB
/
FieldSchema.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
'use strict';
const makeSchema = require('../utils/makeSchema');
const RefResourceSchema = require('./RefResourceSchema');
const FieldChoicesSchema = require('./FieldChoicesSchema');
const { INCOMPATIBLE_FIELD_SCHEMA_KEYS } = require('../constants');
// the following takes an array of string arrays (string[][]) and returns the follwing string:
// * `a` & `b`
// * `c` & `d`
// ... etc
const wrapInBackticks = (s) => `\`${s}\``;
const formatBullet = (f) => `* ${f.map(wrapInBackticks).join(' & ')}`;
const incompatibleFieldsList =
INCOMPATIBLE_FIELD_SCHEMA_KEYS.map(formatBullet).join('\n');
module.exports = makeSchema(
{
id: '/FieldSchema',
description: `Defines a field an app either needs as input, or gives as output. In addition to the requirements below, the following keys are mutually exclusive:\n\n${incompatibleFieldsList}`,
type: 'object',
required: ['key'],
properties: {
key: {
description:
'A unique machine readable key for this value (IE: "fname").',
type: 'string',
minLength: 1,
},
label: {
description:
'A human readable label for this value (IE: "First Name").',
type: 'string',
minLength: 1,
},
helpText: {
description:
'A human readable description of this value (IE: "The first part of a full name."). You can use Markdown.',
type: 'string',
minLength: 1,
maxLength: 1000,
},
type: {
description:
'The type of this value. Use `string` for basic text input, `text` for a large, `<textarea>` style box, and `code` for a `<textarea>` with a fixed-width font.',
type: 'string',
// string == unicode
// text == a long textarea string
// integer == int
// number == float
enum: [
'string',
'text',
'integer',
'number',
'boolean',
'datetime',
'file',
'password',
'copy',
'code',
],
},
required: {
description: 'If this value is required or not.',
type: 'boolean',
},
placeholder: {
description: 'An example value that is not saved.',
type: 'string',
minLength: 1,
},
default: {
description:
'A default value that is saved the first time a Zap is created.',
type: 'string',
minLength: 1,
},
dynamic: {
description:
'A reference to a trigger that will power a dynamic dropdown.',
$ref: RefResourceSchema.id,
},
search: {
description:
'A reference to a search that will guide the user to add a search step to populate this field when creating a Zap.',
$ref: RefResourceSchema.id,
},
choices: {
description:
'An object of machine keys and human values to populate a static dropdown.',
$ref: FieldChoicesSchema.id,
},
list: {
description:
'Acts differently when used in inputFields vs. when used in outputFields. In inputFields: Can a user provide multiples of this field? In outputFields: Does this field return an array of items of type `type`?',
type: 'boolean',
},
children: {
type: 'array',
items: { $ref: '/FieldSchema' },
description:
'An array of child fields that define the structure of a sub-object for this field. Usually used for line items.',
minItems: 1,
},
dict: {
description: 'Is this field a key/value input?',
type: 'boolean',
},
computed: {
description:
'Is this field automatically populated (and hidden from the user)?',
type: 'boolean',
},
altersDynamicFields: {
description:
'Does the value of this field affect the definitions of other fields in the set?',
type: 'boolean',
},
inputFormat: {
description:
'Useful when you expect the input to be part of a longer string. Put "{{input}}" in place of the user\'s input (IE: "https://{{input}}.yourdomain.com").',
type: 'string',
// TODO: Check if it contains one and ONLY ONE '{{input}}'
pattern: '^.*{{input}}.*$',
},
},
examples: [
{ key: 'abc' },
{ key: 'abc', choices: { mobile: 'Mobile Phone' } },
{ key: 'abc', choices: ['first', 'second', 'third'] },
{
key: 'abc',
choices: [{ label: 'Red', sample: '#f00', value: '#f00' }],
},
{ key: 'abc', children: [{ key: 'abc' }] },
{ key: 'abc', type: 'integer', helpText: 'neat' },
],
antiExamples: [
{
example: {},
reason: 'Missing required key: key',
},
{
example: { key: 'abc', type: 'loltype' },
reason: 'Invalid value for key: type',
},
{
example: { key: 'abc', choices: {} },
reason: 'Invalid value for key: choices (cannot be empty)',
},
{
example: { key: 'abc', choices: [] },
reason: 'Invalid value for key: choices (cannot be empty)',
},
{
example: { key: 'abc', choices: [3] },
reason:
'Invalid value for key: choices (if an array, must be of either string or FieldChoiceWithLabelSchema)',
},
{
example: { key: 'abc', choices: [{ label: 'Red', value: '#f00' }] },
reason:
'Invalid value for key: choices (if an array of FieldChoiceWithLabelSchema, must provide key `sample`)',
},
{
example: { key: 'abc', choices: 'mobile' },
reason:
'Invalid value for key: choices (must be either object or array)',
},
{
example: { key: 'abc', children: ['$func$2$f$'] },
reason:
'Invalid value for key: children (must be array of FieldSchema)',
},
],
additionalProperties: false,
},
[RefResourceSchema, FieldChoicesSchema]
);