mirror of
https://github.com/usebruno/bruno.git
synced 2024-11-21 23:43:15 +01:00
feat: collection schema definition
This commit is contained in:
parent
013f9f9e3d
commit
b3bf29d6b2
@ -49,7 +49,7 @@ export const loadWorkspacesFromIdb = () => (dispatch) => {
|
||||
|
||||
export const addWorkspace = (workspaceName) => (dispatch) => {
|
||||
const newWorkspace = {
|
||||
uid: uuid() + "junk",
|
||||
uid: uuid(),
|
||||
name: workspaceName
|
||||
};
|
||||
|
||||
|
64
packages/bruno-schema/src/collections/index.js
Normal file
64
packages/bruno-schema/src/collections/index.js
Normal file
@ -0,0 +1,64 @@
|
||||
const Yup = require('yup');
|
||||
const { uidSchema } = require("../common");
|
||||
|
||||
const keyValueSchema = Yup.object({
|
||||
uid: uidSchema,
|
||||
name: Yup.string().nullable().max(256, 'name must be 256 characters or less').defined(),
|
||||
value: Yup.string().nullable().max(2048, 'value must be 2048 characters or less').defined(),
|
||||
description: Yup.string().nullable().max(2048, 'description must be 2048 characters or less').defined(),
|
||||
enabled: Yup.boolean().defined()
|
||||
}).noUnknown(true).strict();
|
||||
|
||||
const requestTypeSchema = Yup.string().oneOf(['http', 'graphql']).required('type is required');
|
||||
const requestUrlSchema = Yup.string().min(0).max(2048, 'name must be 2048 characters or less').defined();
|
||||
const requestMethodSchema = Yup.string().oneOf(['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'HEAD']).required('method is required');
|
||||
|
||||
const requestBodySchema = Yup.object({
|
||||
mode: Yup.string().oneOf(['none', 'json', 'text', 'xml', 'formUrlEncoded', 'multipartForm']).required('mode is required'),
|
||||
json: Yup.string().max(10240, 'json must be 10240 characters or less'),
|
||||
text: Yup.string().max(10240, 'text must be 10240 characters or less'),
|
||||
xml: Yup.string().max(10240, 'xml must be 10240 characters or less'),
|
||||
formUrlEncoded: keyValueSchema,
|
||||
multipartForm: keyValueSchema,
|
||||
}).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({
|
||||
type: requestTypeSchema,
|
||||
url: requestUrlSchema,
|
||||
method: requestMethodSchema,
|
||||
headers: Yup.array().of(keyValueSchema).required('headers are required'),
|
||||
params: Yup.array().of(keyValueSchema).required('params are required'),
|
||||
body: requestBodySchema
|
||||
}).noUnknown(true).strict();
|
||||
|
||||
const itemSchema = Yup.object({
|
||||
uid: uidSchema,
|
||||
type: Yup.string().oneOf(['request', 'folder']).required('type is required'),
|
||||
name: Yup.string()
|
||||
.min(1, 'name must be atleast 1 characters')
|
||||
.max(50, 'name must be 100 characters or less')
|
||||
.required('name is required'),
|
||||
request: requestSchema.when('type', {
|
||||
is: 'request',
|
||||
then: (schema) => schema.required('request is required when item-type is request')
|
||||
}),
|
||||
items: Yup.lazy(() => Yup.array().of(itemSchema))
|
||||
}).noUnknown(true).strict();
|
||||
|
||||
const collectionSchema = Yup.object({
|
||||
uid: uidSchema,
|
||||
name: Yup.string()
|
||||
.min(1, 'name must be atleast 1 characters')
|
||||
.max(50, 'name must be 100 characters or less')
|
||||
.required('name is required'),
|
||||
items: Yup.array().of(itemSchema)
|
||||
}).noUnknown(true).strict();
|
||||
|
||||
module.exports = {
|
||||
requestSchema,
|
||||
itemSchema,
|
||||
collectionSchema
|
||||
};
|
120
packages/bruno-schema/src/collections/index.spec.js
Normal file
120
packages/bruno-schema/src/collections/index.spec.js
Normal file
@ -0,0 +1,120 @@
|
||||
const { expect } = require('@jest/globals');
|
||||
const { uuid } = require("../utils/testUtils");
|
||||
const { collectionSchema } = require("./index");
|
||||
|
||||
describe('Collection Schema Validation', () => {
|
||||
it('collection schema must validate successfully - simple collection, no items', async () => {
|
||||
const collection = {
|
||||
uid: uuid(),
|
||||
name: 'My Collection'
|
||||
};
|
||||
|
||||
const isValid = await collectionSchema.validate(collection);
|
||||
expect(isValid).toBeTruthy();
|
||||
});
|
||||
|
||||
it('collection schema must validate successfully - simple collection, empty items', async () => {
|
||||
const collection = {
|
||||
uid: uuid(),
|
||||
name: 'My Collection',
|
||||
items: []
|
||||
};
|
||||
|
||||
const isValid = await collectionSchema.validate(collection);
|
||||
expect(isValid).toBeTruthy();
|
||||
});
|
||||
|
||||
it('collection schema must validate successfully - simple collection, just a folder item', async () => {
|
||||
const collection = {
|
||||
uid: uuid(),
|
||||
name: 'My Collection',
|
||||
items: [{
|
||||
uid: uuid(),
|
||||
name: 'A Folder',
|
||||
type: 'folder'
|
||||
}]
|
||||
};
|
||||
|
||||
const isValid = await collectionSchema.validate(collection);
|
||||
expect(isValid).toBeTruthy();
|
||||
});
|
||||
|
||||
it('collection schema must validate successfully - simple collection, just a request item', async () => {
|
||||
const collection = {
|
||||
uid: uuid(),
|
||||
name: 'My Collection',
|
||||
items: [{
|
||||
uid: uuid(),
|
||||
name: 'Get Countries',
|
||||
type: 'request',
|
||||
request: {
|
||||
type: 'http',
|
||||
url: 'https://restcountries.com/v2/alpha/in',
|
||||
method: 'GET',
|
||||
headers: [],
|
||||
params: [],
|
||||
body: {
|
||||
mode: 'none'
|
||||
}
|
||||
}
|
||||
}]
|
||||
};
|
||||
|
||||
const isValid = await collectionSchema.validate(collection);
|
||||
expect(isValid).toBeTruthy();
|
||||
});
|
||||
|
||||
it('collection schema must validate successfully - simple collection, folder inside folder', async () => {
|
||||
const collection = {
|
||||
uid: uuid(),
|
||||
name: 'My Collection',
|
||||
items: [{
|
||||
uid: uuid(),
|
||||
name: 'First Level Folder',
|
||||
type: 'folder',
|
||||
items: [{
|
||||
uid: uuid(),
|
||||
name: 'Second Level Folder',
|
||||
type: 'folder'
|
||||
}]
|
||||
}]
|
||||
};
|
||||
|
||||
const isValid = await collectionSchema.validate(collection);
|
||||
expect(isValid).toBeTruthy();
|
||||
});
|
||||
|
||||
it('collection schema must validate successfully - simple collection, [folder] [request + folder]', async () => {
|
||||
const collection = {
|
||||
uid: uuid(),
|
||||
name: 'My Collection',
|
||||
items: [{
|
||||
uid: uuid(),
|
||||
name: 'First Level Folder',
|
||||
type: 'folder',
|
||||
items: [{
|
||||
uid: uuid(),
|
||||
name: 'Get Countries',
|
||||
type: 'request',
|
||||
request: {
|
||||
type: 'http',
|
||||
url: 'https://restcountries.com/v2/alpha/in',
|
||||
method: 'GET',
|
||||
headers: [],
|
||||
params: [],
|
||||
body: {
|
||||
mode: 'none'
|
||||
}
|
||||
}
|
||||
}, {
|
||||
uid: uuid(),
|
||||
name: 'Second Level Folder',
|
||||
type: 'folder'
|
||||
}]
|
||||
}]
|
||||
};
|
||||
|
||||
const isValid = await collectionSchema.validate(collection);
|
||||
expect(isValid).toBeTruthy();
|
||||
});
|
||||
});
|
57
packages/bruno-schema/src/collections/itemSchema.spec.js
Normal file
57
packages/bruno-schema/src/collections/itemSchema.spec.js
Normal file
@ -0,0 +1,57 @@
|
||||
const { expect } = require('@jest/globals');
|
||||
const { uuid, validationErrorWithMessages } = require("../utils/testUtils");
|
||||
const { itemSchema } = require("./index");
|
||||
|
||||
describe('Item Schema Validation', () => {
|
||||
it('item schema must validate successfully - simple items', async () => {
|
||||
const item = {
|
||||
uid: uuid(),
|
||||
name: 'A Folder',
|
||||
type: 'folder'
|
||||
};
|
||||
|
||||
const isValid = await itemSchema.validate(item);
|
||||
expect(isValid).toBeTruthy();
|
||||
});
|
||||
|
||||
it('item schema must throw an error if name is missing', async () => {
|
||||
const item = {
|
||||
uid: uuid(),
|
||||
type: 'folder'
|
||||
};
|
||||
|
||||
return Promise.all([
|
||||
expect(itemSchema.validate(item)).rejects.toEqual(
|
||||
validationErrorWithMessages('name is required')
|
||||
)
|
||||
]);
|
||||
});
|
||||
|
||||
it('item schema must throw an error if name is empty', async () => {
|
||||
const item = {
|
||||
uid: uuid(),
|
||||
name: '',
|
||||
type: 'folder'
|
||||
};
|
||||
|
||||
return Promise.all([
|
||||
expect(itemSchema.validate(item)).rejects.toEqual(
|
||||
validationErrorWithMessages('name must be atleast 1 characters')
|
||||
)
|
||||
]);
|
||||
});
|
||||
|
||||
it('item schema must throw an error if request is not present when item-type is request', async () => {
|
||||
const item = {
|
||||
uid: uuid(),
|
||||
name: 'Get Users',
|
||||
type: 'request'
|
||||
};
|
||||
|
||||
return Promise.all([
|
||||
expect(itemSchema.validate(item)).rejects.toEqual(
|
||||
validationErrorWithMessages('request is required when item-type is request')
|
||||
)
|
||||
]);
|
||||
});
|
||||
});
|
107
packages/bruno-schema/src/collections/requestSchema.spec.js
Normal file
107
packages/bruno-schema/src/collections/requestSchema.spec.js
Normal file
@ -0,0 +1,107 @@
|
||||
const { expect } = require('@jest/globals');
|
||||
const { uuid, validationErrorWithMessages } = require("../utils/testUtils");
|
||||
const { requestSchema } = require("./index");
|
||||
|
||||
describe('Request Schema Validation', () => {
|
||||
it('request schema must validate successfully - simple request', async () => {
|
||||
const request = {
|
||||
type: 'http',
|
||||
url: 'https://restcountries.com/v2/alpha/in',
|
||||
method: 'GET',
|
||||
headers: [],
|
||||
params: [],
|
||||
body: {
|
||||
mode: 'none'
|
||||
}
|
||||
};
|
||||
|
||||
const isValid = await requestSchema.validate(request);
|
||||
expect(isValid).toBeTruthy();
|
||||
});
|
||||
|
||||
it('request schema must throw an error of type is invalid', async () => {
|
||||
const request = {
|
||||
type: 'http-junk',
|
||||
url: 'https://restcountries.com/v2/alpha/in',
|
||||
method: 'GET',
|
||||
headers: [],
|
||||
params: [],
|
||||
body: {
|
||||
mode: 'none'
|
||||
}
|
||||
};
|
||||
|
||||
return Promise.all([
|
||||
expect(requestSchema.validate(request)).rejects.toEqual(
|
||||
validationErrorWithMessages('type must be one of the following values: http, graphql')
|
||||
)
|
||||
]);
|
||||
});
|
||||
|
||||
it('request schema must throw an error of method is invalid', async () => {
|
||||
const request = {
|
||||
type: 'http',
|
||||
url: 'https://restcountries.com/v2/alpha/in',
|
||||
method: 'GET-junk',
|
||||
headers: [],
|
||||
params: [],
|
||||
body: {
|
||||
mode: 'none'
|
||||
}
|
||||
};
|
||||
|
||||
return Promise.all([
|
||||
expect(requestSchema.validate(request)).rejects.toEqual(
|
||||
validationErrorWithMessages('method must be one of the following values: GET, POST, PUT, DELETE, PATCH, HEAD')
|
||||
)
|
||||
]);
|
||||
});
|
||||
|
||||
it('request schema must throw an error of header name is missing', async () => {
|
||||
const request = {
|
||||
type: 'http',
|
||||
url: 'https://restcountries.com/v2/alpha/in',
|
||||
method: 'GET',
|
||||
headers: [{
|
||||
uid: uuid(),
|
||||
value: 'Check',
|
||||
description: '',
|
||||
enabled: true
|
||||
}],
|
||||
params: [],
|
||||
body: {
|
||||
mode: 'none'
|
||||
}
|
||||
};
|
||||
|
||||
return Promise.all([
|
||||
expect(requestSchema.validate(request)).rejects.toEqual(
|
||||
validationErrorWithMessages('headers[0].name must be defined')
|
||||
)
|
||||
]);
|
||||
});
|
||||
|
||||
it('request schema must throw an error of param value is missing', async () => {
|
||||
const request = {
|
||||
type: 'http',
|
||||
url: 'https://restcountries.com/v2/alpha/in',
|
||||
method: 'GET',
|
||||
headers: [],
|
||||
params: [{
|
||||
uid: uuid(),
|
||||
name: 'customerId',
|
||||
description: '',
|
||||
enabled: true
|
||||
}],
|
||||
body: {
|
||||
mode: 'none'
|
||||
}
|
||||
};
|
||||
|
||||
return Promise.all([
|
||||
expect(requestSchema.validate(request)).rejects.toEqual(
|
||||
validationErrorWithMessages('params[0].value must be defined')
|
||||
)
|
||||
]);
|
||||
});
|
||||
});
|
@ -3,6 +3,7 @@ const Yup = require('yup');
|
||||
const uidSchema = Yup.string()
|
||||
.length(21, 'uid must be 21 characters in length')
|
||||
.matches(/^[a-zA-Z0-9]*$/, 'uid must be alphanumeric')
|
||||
.required('uid is required')
|
||||
.strict();
|
||||
|
||||
module.exports = {
|
||||
|
Loading…
Reference in New Issue
Block a user