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) => {
|
export const addWorkspace = (workspaceName) => (dispatch) => {
|
||||||
const newWorkspace = {
|
const newWorkspace = {
|
||||||
uid: uuid() + "junk",
|
uid: uuid(),
|
||||||
name: workspaceName
|
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()
|
const uidSchema = Yup.string()
|
||||||
.length(21, 'uid must be 21 characters in length')
|
.length(21, 'uid must be 21 characters in length')
|
||||||
.matches(/^[a-zA-Z0-9]*$/, 'uid must be alphanumeric')
|
.matches(/^[a-zA-Z0-9]*$/, 'uid must be alphanumeric')
|
||||||
|
.required('uid is required')
|
||||||
.strict();
|
.strict();
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
Loading…
Reference in New Issue
Block a user