Files
bruno/packages/bruno-schema/src/collections/index.js
2025-04-03 13:48:39 +05:30

436 lines
13 KiB
JavaScript

const Yup = require('yup');
const { uidSchema } = require('../common');
const environmentVariablesSchema = Yup.object({
uid: uidSchema,
name: Yup.string().nullable(),
value: Yup.string().nullable(),
type: Yup.string().oneOf(['text']).required('type is required'),
enabled: Yup.boolean().defined(),
secret: Yup.boolean()
})
.noUnknown(true)
.strict();
const environmentSchema = Yup.object({
uid: uidSchema,
name: Yup.string().min(1).required('name is required'),
variables: Yup.array().of(environmentVariablesSchema).required('variables are required')
})
.noUnknown(true)
.strict();
const environmentsSchema = Yup.array().of(environmentSchema);
const keyValueSchema = Yup.object({
uid: uidSchema,
name: Yup.string().nullable(),
value: Yup.string().nullable(),
description: Yup.string().nullable(),
enabled: Yup.boolean()
})
.noUnknown(true)
.strict();
const varsSchema = Yup.object({
uid: uidSchema,
name: Yup.string().nullable(),
value: Yup.string().nullable(),
description: Yup.string().nullable(),
enabled: Yup.boolean(),
// todo
// anoop(4 feb 2023) - nobody uses this, and it needs to be removed
local: Yup.boolean()
})
.noUnknown(true)
.strict();
const requestUrlSchema = Yup.string().min(0).defined();
const requestMethodSchema = Yup.string()
.oneOf(['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'HEAD', 'OPTIONS', 'TRACE'])
.required('method is required');
const graphqlBodySchema = Yup.object({
query: Yup.string().nullable(),
variables: Yup.string().nullable()
})
.noUnknown(true)
.strict();
const multipartFormSchema = Yup.object({
uid: uidSchema,
type: Yup.string().oneOf(['file', 'text']).required('type is required'),
name: Yup.string().nullable(),
value: Yup.mixed().when('type', {
is: 'file',
then: Yup.array().of(Yup.string().nullable()).nullable(),
otherwise: Yup.string().nullable()
}),
description: Yup.string().nullable(),
contentType: Yup.string().nullable(),
enabled: Yup.boolean()
})
.noUnknown(true)
.strict();
const fileSchema = Yup.object({
uid: uidSchema,
filePath: Yup.string().nullable(),
contentType: Yup.string().nullable(),
selected: Yup.boolean()
})
.noUnknown(true)
.strict();
const requestBodySchema = Yup.object({
mode: Yup.string()
.oneOf(['none', 'json', 'text', 'xml', 'formUrlEncoded', 'multipartForm', 'graphql', 'sparql', 'file'])
.required('mode is required'),
json: Yup.string().nullable(),
text: Yup.string().nullable(),
xml: Yup.string().nullable(),
sparql: Yup.string().nullable(),
formUrlEncoded: Yup.array().of(keyValueSchema).nullable(),
multipartForm: Yup.array().of(multipartFormSchema).nullable(),
graphql: graphqlBodySchema.nullable(),
file: Yup.array().of(fileSchema).nullable()
})
.noUnknown(true)
.strict();
const authAwsV4Schema = Yup.object({
accessKeyId: Yup.string().nullable(),
secretAccessKey: Yup.string().nullable(),
sessionToken: Yup.string().nullable(),
service: Yup.string().nullable(),
region: Yup.string().nullable(),
profileName: Yup.string().nullable()
})
.noUnknown(true)
.strict();
const authBasicSchema = Yup.object({
username: Yup.string().nullable(),
password: Yup.string().nullable()
})
.noUnknown(true)
.strict();
const authWsseSchema = Yup.object({
username: Yup.string().nullable(),
password: Yup.string().nullable()
})
.noUnknown(true)
.strict();
const authBearerSchema = Yup.object({
token: Yup.string().nullable()
})
.noUnknown(true)
.strict();
const authDigestSchema = Yup.object({
username: Yup.string().nullable(),
password: Yup.string().nullable()
})
.noUnknown(true)
.strict();
const authNTLMSchema = Yup.object({
username: Yup.string().nullable(),
password: Yup.string().nullable(),
domain: Yup.string().nullable()
})
.noUnknown(true)
.strict();
const authApiKeySchema = Yup.object({
key: Yup.string().nullable(),
value: Yup.string().nullable(),
placement: Yup.string().oneOf(['header', 'queryparams']).nullable()
})
.noUnknown(true)
.strict();
const oauth2AuthorizationAdditionalParametersSchema = Yup.object({
name: Yup.string().nullable(),
value: Yup.string().nullable(),
sendIn: Yup.string()
.oneOf(['headers', 'queryparams'])
.required('send in property is required'),
enabled: Yup.boolean()
})
.noUnknown(true)
.strict();
const oauth2AdditionalParametersSchema = Yup.object({
name: Yup.string().nullable(),
value: Yup.string().nullable(),
sendIn: Yup.string()
.oneOf(['headers', 'queryparams', 'body'])
.required('send in property is required'),
enabled: Yup.boolean()
})
.noUnknown(true)
.strict();
const oauth2Schema = Yup.object({
grantType: Yup.string()
.oneOf(['client_credentials', 'password', 'authorization_code'])
.required('grantType is required'),
username: Yup.string().when('grantType', {
is: (val) => ['client_credentials', 'password'].includes(val),
then: Yup.string().nullable(),
otherwise: Yup.string().nullable().strip()
}),
password: Yup.string().when('grantType', {
is: (val) => ['client_credentials', 'password'].includes(val),
then: Yup.string().nullable(),
otherwise: Yup.string().nullable().strip()
}),
callbackUrl: Yup.string().when('grantType', {
is: (val) => ['authorization_code'].includes(val),
then: Yup.string().nullable(),
otherwise: Yup.string().nullable().strip()
}),
authorizationUrl: Yup.string().when('grantType', {
is: (val) => ['authorization_code'].includes(val),
then: Yup.string().nullable(),
otherwise: Yup.string().nullable().strip()
}),
accessTokenUrl: Yup.string().when('grantType', {
is: (val) => ['client_credentials', 'password', 'authorization_code'].includes(val),
then: Yup.string().nullable(),
otherwise: Yup.string().nullable().strip()
}),
clientId: Yup.string().when('grantType', {
is: (val) => ['client_credentials', 'password', 'authorization_code'].includes(val),
then: Yup.string().nullable(),
otherwise: Yup.string().nullable().strip()
}),
clientSecret: Yup.string().when('grantType', {
is: (val) => ['client_credentials', 'password', 'authorization_code'].includes(val),
then: Yup.string().nullable(),
otherwise: Yup.string().nullable().strip()
}),
scope: Yup.string().when('grantType', {
is: (val) => ['client_credentials', 'password', 'authorization_code'].includes(val),
then: Yup.string().nullable(),
otherwise: Yup.string().nullable().strip()
}),
state: Yup.string().when('grantType', {
is: (val) => ['authorization_code'].includes(val),
then: Yup.string().nullable(),
otherwise: Yup.string().nullable().strip()
}),
pkce: Yup.boolean().when('grantType', {
is: (val) => ['authorization_code'].includes(val),
then: Yup.boolean().default(false),
otherwise: Yup.boolean()
}),
credentialsPlacement: Yup.string().when('grantType', {
is: (val) => ['client_credentials', 'password', 'authorization_code'].includes(val),
then: Yup.string().nullable(),
otherwise: Yup.string().nullable().strip()
}),
credentialsId: Yup.string().when('grantType', {
is: (val) => ['client_credentials', 'password', 'authorization_code'].includes(val),
then: Yup.string().nullable(),
otherwise: Yup.string().nullable().strip()
}),
tokenPlacement: Yup.string().when('grantType', {
is: (val) => ['client_credentials', 'password', 'authorization_code'].includes(val),
then: Yup.string().nullable(),
otherwise: Yup.string().nullable().strip()
}),
tokenHeaderPrefix: Yup.string().when(['grantType', 'tokenPlacement'], {
is: (grantType, tokenPlacement) =>
['client_credentials', 'password', 'authorization_code'].includes(grantType) && tokenPlacement === 'header',
then: Yup.string().nullable(),
otherwise: Yup.string().nullable().strip()
}),
tokenQueryKey: Yup.string().when(['grantType', 'tokenPlacement'], {
is: (grantType, tokenPlacement) =>
['client_credentials', 'password', 'authorization_code'].includes(grantType) && tokenPlacement === 'url',
then: Yup.string().nullable(),
otherwise: Yup.string().nullable().strip()
}),
refreshTokenUrl: Yup.string().when('grantType', {
is: (val) => ['client_credentials', 'password', 'authorization_code'].includes(val),
then: Yup.string().nullable(),
otherwise: Yup.string().nullable().strip()
}),
autoRefreshToken: Yup.boolean().when('grantType', {
is: (val) => ['client_credentials', 'password', 'authorization_code'].includes(val),
then: Yup.boolean().default(false),
otherwise: Yup.boolean()
}),
autoFetchToken: Yup.boolean().when('grantType', {
is: (val) => ['authorization_code'].includes(val),
then: Yup.boolean().default(true),
otherwise: Yup.boolean()
}),
additionalParameters: Yup.object({
authorization: Yup.array().of(oauth2AuthorizationAdditionalParametersSchema).optional(),
token: Yup.array().of(oauth2AdditionalParametersSchema).optional(),
refresh: Yup.array().of(oauth2AdditionalParametersSchema).optional()
})
})
.noUnknown(true)
.strict();
const authSchema = Yup.object({
mode: Yup.string()
.oneOf(['inherit', 'none', 'awsv4', 'basic', 'bearer', 'digest', 'ntlm', 'oauth2', 'wsse', 'apikey'])
.required('mode is required'),
awsv4: authAwsV4Schema.nullable(),
basic: authBasicSchema.nullable(),
bearer: authBearerSchema.nullable(),
ntlm: authNTLMSchema.nullable(),
digest: authDigestSchema.nullable(),
oauth2: oauth2Schema.nullable(),
wsse: authWsseSchema.nullable(),
apikey: authApiKeySchema.nullable()
})
.noUnknown(true)
.strict()
.nullable();
const requestParamsSchema = Yup.object({
uid: uidSchema,
name: Yup.string().nullable(),
value: Yup.string().nullable(),
description: Yup.string().nullable(),
type: Yup.string().oneOf(['query', 'path']).required('type is required'),
enabled: Yup.boolean()
})
.noUnknown(true)
.strict();
// Right now, the request schema is very tightly coupled with http request
// As we introduce more request types in the future, we will improve the definition to support
// schema structure based on other request type
const requestSchema = Yup.object({
url: requestUrlSchema,
method: requestMethodSchema,
headers: Yup.array().of(keyValueSchema).required('headers are required'),
params: Yup.array().of(requestParamsSchema).required('params are required'),
auth: authSchema,
body: requestBodySchema,
script: Yup.object({
req: Yup.string().nullable(),
res: Yup.string().nullable()
})
.noUnknown(true)
.strict(),
vars: Yup.object({
req: Yup.array().of(varsSchema).nullable(),
res: Yup.array().of(varsSchema).nullable()
})
.noUnknown(true)
.strict()
.nullable(),
assertions: Yup.array().of(keyValueSchema).nullable(),
tests: Yup.string().nullable(),
docs: Yup.string().nullable()
})
.noUnknown(true)
.strict();
const folderRootSchema = Yup.object({
request: Yup.object({
headers: Yup.array().of(keyValueSchema).nullable(),
auth: authSchema,
script: Yup.object({
req: Yup.string().nullable(),
res: Yup.string().nullable()
})
.noUnknown(true)
.strict()
.nullable(),
vars: Yup.object({
req: Yup.array().of(varsSchema).nullable(),
res: Yup.array().of(varsSchema).nullable()
})
.noUnknown(true)
.strict()
.nullable(),
tests: Yup.string().nullable()
})
.noUnknown(true)
.strict()
.nullable(),
docs: Yup.string().nullable(),
meta: Yup.object({
name: Yup.string().nullable()
})
.noUnknown(true)
.strict()
.nullable()
})
.noUnknown(true)
.nullable();
const itemSchema = Yup.object({
uid: uidSchema,
type: Yup.string().oneOf(['http-request', 'graphql-request', 'folder', 'js']).required('type is required'),
seq: Yup.number().min(1),
name: Yup.string().min(1, 'name must be at least 1 character').required('name is required'),
request: requestSchema.when('type', {
is: (type) => ['http-request', 'graphql-request'].includes(type),
then: (schema) => schema.required('request is required when item-type is request')
}),
fileContent: Yup.string().when('type', {
// If the type is 'js', the fileContent field is expected to be a string.
// This can include an empty string, indicating that the JS file may not have any content.
is: 'js',
then: Yup.string(),
// For all other types, the fileContent field is not required and can be null.
otherwise: Yup.string().nullable()
}),
root: Yup.mixed().when('type', {
is: 'folder',
then: folderRootSchema,
otherwise: Yup.mixed().nullable().notRequired()
}),
items: Yup.lazy(() => Yup.array().of(itemSchema)),
filename: Yup.string().nullable(),
pathname: Yup.string().nullable()
})
.noUnknown(true)
.strict();
const collectionSchema = Yup.object({
version: Yup.string().oneOf(['1']).required('version is required'),
uid: uidSchema,
name: Yup.string().min(1, 'name must be at least 1 character').required('name is required'),
items: Yup.array().of(itemSchema),
activeEnvironmentUid: Yup.string()
.length(21, 'activeEnvironmentUid must be 21 characters in length')
.matches(/^[a-zA-Z0-9]*$/, 'uid must be alphanumeric')
.nullable(),
environments: environmentsSchema,
pathname: Yup.string().nullable(),
runnerResult: Yup.object({
items: Yup.array()
}),
runtimeVariables: Yup.object(),
brunoConfig: Yup.object(),
root: folderRootSchema
})
.noUnknown(true)
.strict();
module.exports = {
requestSchema,
itemSchema,
environmentSchema,
environmentsSchema,
collectionSchema
};