From 67f7ab075a83d01ef3bd8d5b48eeda8988d447c3 Mon Sep 17 00:00:00 2001 From: Nikiroy78 Date: Tue, 11 Oct 2022 08:16:40 +0300 Subject: [PATCH] add files into repos --- codeExample.js | 44 ++++++++++--- components/Main.js | 81 ++++++++++++------------ components/Method.js | 140 +++++++++++++++++++++++++++++------------- components/Session.js | 32 +++++++++- components/types.js | 80 ++++++++++++++++++++++-- 5 files changed, 282 insertions(+), 95 deletions(-) diff --git a/codeExample.js b/codeExample.js index 049d5aa..166a529 100644 --- a/codeExample.js +++ b/codeExample.js @@ -2,7 +2,7 @@ const backend = require('./index'); // Создаём класс бэкенда, наследующий класс backend.Main class Main extends backend.Main { - session (params, sessionData) { // Настраиваем сессию (опционально) + session (params, sessionData) { // Настраиваем сессию (опционально) sessionData._setValue('example', 1); // Задать значение console.log(sessionData.example); // Получить значение из сессии sessionData._remove('example'); // Убрать значение @@ -42,7 +42,7 @@ server.typeError = 'param {param} must be only {long_type} ({short_type})'; // Создаём класс группы методов class ExampleMethodGroup extends backend.Group { - handler (params, session) { // Путевая обработка + handler (params, session) { // Путевая обработка session._setValue('example', 1); // Задать значение console.log(session.example); // Получить значение из сессии session._remove('example'); // Убрать значение @@ -50,7 +50,7 @@ class ExampleMethodGroup extends backend.Group { return 'Example error' // Пример ошибки } } -// Создаём класс метода +// Создаём классы методов class ExampleMethod extends backend.Method { /* var result = this.MainObject.call(method : string, params : object) // Вызов подключённого метода @@ -58,18 +58,46 @@ class ExampleMethod extends backend.Method { // Обработчик параметров execute (params) { - console.log(params); - return params.text; - throw { code: 'EXAMPLE_ERROR', details: new Object() }; + return { + text : params.text, + result : this.MainObject.call('sum', { + a : 15, + b : 17 + }) + }; + throw new backend.ApiError('EXAMPLE_ERROR', new Object()); } } + +class SumMethod extends backend.Method { + execute (params) { + return params.a + params.b; + } +} + +// Создаём экземпляры классов +var sumMethod = new SumMethod('sum', '/sum', { + a : { + required : true, + type : backend.types.integer, + conversion : false, + // allow_methods : ['post'], + }, + b : { + required : true, + type : backend.types.integer, + conversion : false, + // allow_methods : ['post'], + } +}); + var exampleMethod = new ExampleMethod('example', '/example', { text: { required : true, type : backend.types.string, conversion : false, - // values : ['123', 'test'], + values : ['123', 'test'], min_length : 1, max_length : 255, // allow_methods : ['post'], @@ -78,8 +106,10 @@ var exampleMethod = new ExampleMethod('example', '/example', { }); // Привяжем метод к группе exampleMethod.group(ExampleMethodGroup); +sumMethod.group(ExampleMethodGroup); // Привяжем метод к основному проекту server.method(exampleMethod); +server.method(sumMethod); // Запускаем сервер server.server('/api/v1'/*, { Информация о SSL }*/).listen(8080, async (err) => { diff --git a/components/Main.js b/components/Main.js index 905730c..5c0c0b0 100644 --- a/components/Main.js +++ b/components/Main.js @@ -17,54 +17,55 @@ class Main { constructor (sendHeaders = true) { this.sendHeaders = sendHeaders; this.methods = new Object(); - - this.errorHadler = (error) => { - let errorData; - if (error.name == "API Error") { - errorData = { - code : error.message, - details : error.data - }; - } - else { - errorData = { - name : error.name, - stack : error.stack - }; - } - return { - mainbody : { error : errorData }, - headers : {}, - cookies : {}, - // redirect_uri: ''; // if want redirect to another url - code: 400 - } - }; - - this.responseHandler = (response) => ({ - mainbody : { response }, - headers : {}, - cookies : {}, - // redirect_uri: ''; // if want redirect to another url - code: 200 - }); - - this.paramsError = (required, additional) => { - return new ApiError('UNSYNTAX_OR_MISSED_REQUIRED_PARAMS', { required, additional }); - }; - this.typeError = 'param {param} must be only {long_type} ({short_type})'; } + paramsError (required, additional) { + return new ApiError('UNSYNTAX_OR_MISSED_REQUIRED_PARAMS', { required, additional }); + } + + errorHadler (error) { + let errorData; + if (error.name == "API Error") { + errorData = { + code : error.message, + details : error.data + }; + } + else { + errorData = { + name : error.name, + stack : error.stack + }; + } + return { + mainbody : { error : errorData }, + headers : {}, + cookies : {}, + // redirect_uri: ''; // if want redirect to another url + code: 400 + } + } + + session (params, sessionData) { return 1; } + + responseHandler (result) { return ({ + mainbody : { result }, + headers : {}, + cookies : {}, + // redirect_uri: ''; // if want redirect to another url + code: 200 + }) }; + method (methodObj) { methodObj._pinMain(this); this.methods[methodObj.name] = methodObj; } call (method, params) { - return this.methods[method.name].pre_execute(params); + return this.methods[method].pre_execute(params); } - router (returnMiddlewareFunction = false, middlewareFunction = (req, res, next) => next(), debug = (text) => {}) { + router (returnMiddlewareFunction = false, middlewareFunction = (req, res, next) => next(), debug = (text) => console.log(text)) { let router = express.Router(); router.use(require('cookie-parser')()); // parse various different custom JSON types as JSON @@ -84,7 +85,7 @@ class Main { } try { let contentType = req.get('Content-Type'); - if (!!contentType) { + if (contentType != undefined) { contentType = contentType.toLowerCase(); } else { @@ -92,6 +93,7 @@ class Main { } let result = this.methods[name].executeIntoExpressRouter( + this.methods[name].allowedMethods[methodId], req.headers, (contentType.indexOf('json') != -1) ? req.body : new Object(), req.params, @@ -105,6 +107,7 @@ class Main { ); if (!handledDataResponse.redirect_uri) { for (let header in handledDataResponse.headers) { + console.log('>>header', header); res.set(header, handledDataResponse.headers[header].toString()); } for (let cookie in handledDataResponse.cookies) { diff --git a/components/Method.js b/components/Method.js index 1a1af4c..863ce95 100644 --- a/components/Method.js +++ b/components/Method.js @@ -1,4 +1,31 @@ const typesApi = require('./types'); +const Session = require('./Session'); + + +const formatMessage = (message, errorType, scheme, value, param) => { + value = String(value); + switch (errorType) { + case 'typeError' : + return message.split('{param}').join(param).split('{long_type}').join(scheme.type.long_name).split('{short_type}').join(scheme.type.short_name); + case 'valuesError' : + return message.split('{param}').join(param).split('{value}').join(value).split('{values}').join(scheme.values.join(', ')); + case 'minLengthError' : + return message.split('{value}').join(scheme.min_length); + case 'maxLengthError' : + return message.split('{value}').join(scheme.max_length); + case 'httpMethodError' : + return message.split('{method}').join(value).split('{methods}').join(scheme.allow_methods.join(', ')); + } +}; + + +const compileParams = (params) => { + let compiled = new Object(); + for (let objKey in params) { + compiled = Object.assign(compiled, params[objKey]); + } + return compiled; +} class Method { @@ -8,8 +35,15 @@ class Method { this.paramsCompiles = params; this.groupsConnected = new Array(); this.allowedMethods = new Array(); - this.typeError = 'param {param} must be only {long_type} ({short_type})'; - this.valuesError = 'value (({value})) not finded into {values}'; + // Errors + this.error = new Object(); + this.error.typeError = 'param {param} must be only {long_type} ({short_type})'; + this.error.valuesError = 'value "{value}" not finded into {values}'; + this.error.minLengthError = 'value must be more or equal {value}'; + this.error.maxLengthError = 'value must be less or equal {value}'; + this.error.httpMethodError = 'Method {method} does not supported. Methods supports: {methods}'; + + this.isDynamic = false; // Setting allow methods let allowedMethods; @@ -26,11 +60,16 @@ class Method { } } + useDynamicType (condition) { + this.isDynamic = !!condition; + } + _pinMain (mainObject) { this.MainObject = mainObject; } executeIntoExpressRouter ( + currentMethod, headers, json, params, @@ -39,37 +78,58 @@ class Method { files, cookies ) { + // Execute session + let sessionObject = new Session(); + this.MainObject.session(compileParams({ + headers, + json, + params, + query, + body, + files, + cookies + }), sessionObject); + let values = sessionObject._getValues(); + for (let key in values) { paramsEndless[key] = values[key]; } + let paramsEndless = new Object(); let paramScheme; let required = { missed : [], unsyntax : [] }; let additional = { missed : [], unsyntax : [] }; - let checkKeys = new Array(); + let checkKeys; for (let param in this.paramsCompiles) { + checkKeys = new Array(); paramScheme = { required : false, - type : typesApi.unknown, + type : this.isDynamic ? typesApi.dynamic : typesApi.unknown, allow_methods : ['get', 'post', 'put', 'delete'], + conversion : false, allow_params : ['headers', 'json', 'params', 'query', 'body', 'files', 'cookies'] }; // Configure paramScheme for (let key in this.paramsCompiles[param]) { paramScheme[key] = this.paramsCompiles[param][key]; } // check missible - if (headers[param] !== undefined & paramScheme.allow_params.indexOf('headers') != -1) { checkKeys.push('headers'); } - if (json[param] !== undefined & paramScheme.allow_params.indexOf('json') != -1) { checkKeys.push('json'); } - if (query[param] !== undefined & paramScheme.allow_params.indexOf('query') != -1) { checkKeys.push('query'); } - if (body[param] !== undefined & paramScheme.allow_params.indexOf('body') != -1) { checkKeys.push('body'); } - if (files[param] !== undefined & paramScheme.allow_params.indexOf('files') != -1) { checkKeys.push('files'); } - if (cookies[param] !== undefined & paramScheme.allow_params.indexOf('cookies') != -1) { checkKeys.push('cookies'); } - if (params[param] !== undefined & paramScheme.allow_params.indexOf('params') != -1) { checkKeys.push('params'); } + if (headers[param] != undefined & paramScheme.allow_params.indexOf('headers') != -1) { checkKeys.push('headers'); } + if (json[param] != undefined & paramScheme.allow_params.indexOf('json') != -1) { checkKeys.push('json'); } + if (query[param] != undefined & paramScheme.allow_params.indexOf('query') != -1) { checkKeys.push('query'); } + if (body[param] != undefined & paramScheme.allow_params.indexOf('body') != -1) { checkKeys.push('body'); } + if (files[param] != undefined & paramScheme.allow_params.indexOf('files') != -1) { checkKeys.push('files'); } + if (cookies[param] != undefined & paramScheme.allow_params.indexOf('cookies') != -1) { checkKeys.push('cookies'); } + if (params[param] != undefined & paramScheme.allow_params.indexOf('params') != -1) { checkKeys.push('params'); } if (checkKeys.length == 0) { if (paramScheme.required) { required.missed.push(param); } else { additional.missed.push(param); } } + else if (paramScheme.allow_methods.indexOf(currentMethod) == -1) { + if (paramScheme.required) { required.unsyntax.push({param : param, description : formatMessage(this.error.httpMethodError, 'httpMethodError', paramScheme, currentMethod, param) }); } + else { additional.unsyntax.push({param : param, description : formatMessage(this.error.httpMethodError, 'httpMethodError', paramScheme, currentMethod, param)}); } + } else { checkKeys = checkKeys.sort((a, b) => Number(b == 'query' || b == 'cookies') - Number(a == 'query' || a == 'cookies')); let isSyntax; let convertedValue; + let selectedSyntaxError = 'typeError'; for (let key in checkKeys) { switch (checkKeys[key]) { case 'query' : @@ -82,20 +142,20 @@ class Method { [isSyntax, convertedValue] = paramScheme.type.syntax(headers[param], true); break; case 'json' : - [isSyntax, convertedValue] = paramScheme.type.syntax(json[param], false); + [isSyntax, convertedValue] = paramScheme.type.syntax(json[param], paramScheme.conversion); break; case 'params' : - [isSyntax, convertedValue] = paramScheme.type.syntax(params[param], false); + [isSyntax, convertedValue] = paramScheme.type.syntax(params[param], paramScheme.conversion); break; case 'body' : - [isSyntax, convertedValue] = paramScheme.type.syntax(body[param], false); + [isSyntax, convertedValue] = paramScheme.type.syntax(body[param], paramScheme.conversion); break; case 'files' : - [isSyntax, convertedValue] = paramScheme.type.syntax(files[param], false); + [isSyntax, convertedValue] = paramScheme.type.syntax(files[param], paramScheme.conversion); break; } if (isSyntax) { - isSyntax = paramScheme.type.checkSchema(convertedValue, paramScheme); + [isSyntax, selectedSyntaxError] = paramScheme.type.checkSchema(convertedValue, paramScheme); if (isSyntax) break; } } @@ -103,8 +163,8 @@ class Method { paramsEndless[param] = convertedValue; } else { - if (paramScheme.required) { required.unsyntax.push({param : param, description : this.typeError.split('{param}').join(param).split('{long_type}').join(paramScheme.type.long_name).split('{short_type}').join(paramScheme.type.short_name)}); } - else { additional.unsyntax.push({param : param, description : this.typeError.split('{param}').join(param).split('{long_type}').join(paramScheme.type.long_name).split('{short_type}').join(paramScheme.type.short_name)}); } + if (paramScheme.required) { required.unsyntax.push({param : param, description : formatMessage(this.error[selectedSyntaxError], selectedSyntaxError, paramScheme, convertedValue, param) }); } + else { additional.unsyntax.push({param : param, description : formatMessage(this.error[selectedSyntaxError], selectedSyntaxError, paramScheme, convertedValue, param)}); } } } } @@ -121,14 +181,15 @@ class Method { let required = { missed : [], unsyntax : [] }; let additional = { missed : [], unsyntax : [] }; let isSyntax; - let _; + let value; let paramScheme; for (let param in this.paramsCompiles) { paramScheme = { required : false, - type : typesApi.unknown, + type : this.isDynamic ? typesApi.dynamic : typesApi.unknown, allow_methods : ['get', 'post', 'put', 'delete'], + conversion : false, allow_params : ['headers', 'json', 'params', 'query', 'body', 'files', 'cookies'] }; for (let key in this.paramsCompiles[param]) { paramScheme[key] = this.paramsCompiles[param][key]; } @@ -141,31 +202,28 @@ class Method { } } else { - [isSyntax, _] = paramScheme.type(params[param], false); + let selectedSyntaxError = 'typeError'; + [isSyntax, value] = paramScheme.type.syntax(params[param], paramScheme.conversion); if (!isSyntax) { if (paramScheme.required) { - required.unsyntax.push({param : param, description : this.typeError.split('{param}').join(param).split('{long_type}').join(paramScheme.type.long_name).split('{short_type}').join(paramScheme.type.short_name)}); + required.unsyntax.push({param : param, description : this.error.typeError.split('{param}').join(param).split('{long_type}').join(paramScheme.type.long_name).split('{short_type}').join(paramScheme.type.short_name)}); } else { - additional.unsyntax.push({param : param, description : this.typeError.split('{param}').join(param).split('{long_type}').join(paramScheme.type.long_name).split('{short_type}').join(paramScheme.type.short_name)}); + additional.unsyntax.push({param : param, description : this.error.typeError.split('{param}').join(param).split('{long_type}').join(paramScheme.type.long_name).split('{short_type}').join(paramScheme.type.short_name)}); } } else { - if (paramScheme.values != undefined) { - if (paramScheme.values.indexOf(params[param]) == -1) { - if (paramScheme.required) { - required.unsyntax.push({ - param : param, - description : this.valuesError.split('{value}').join(convertedValue.toString()).split('{values}').join(paramScheme.values.join(', ')) - }); - } - else { - additional.unsyntax.push({ - param : param, - description : this.valuesError.split('{value}').join(convertedValue.toString()).split('{values}').join(paramScheme.values.join(', ')) - }); - } + [isSyntax, selectedSyntaxError] = paramScheme.type.checkSchema(value, paramScheme); + if (!isSyntax) { + if (paramScheme.required) { + required.unsyntax.push({param : param, description : formatMessage(this.error[selectedSyntaxError], selectedSyntaxError, paramScheme, value, param)}); } + else { + additional.unsyntax.push({param : param, description : formatMessage(this.error[selectedSyntaxError], selectedSyntaxError, paramScheme, value, param)}); + } + } + else { + params[param] = value; } } } @@ -174,13 +232,9 @@ class Method { if (required.missed.length > 0 || required.unsyntax.length > 0 || additional.unsyntax.length > 0) { throw this.MainObject.paramsError(required, additional); } - else { - return this.execute(params); - } - } - else { - return this.execute(params); } + + return this.execute(params); } group (groupClass) { diff --git a/components/Session.js b/components/Session.js index ab0c014..b9cd319 100644 --- a/components/Session.js +++ b/components/Session.js @@ -1 +1,31 @@ -// \ No newline at end of file +class Session { + constructor () { + this._namesReserved = ['_getValues', '_namesReserved', '_setValue', '_remove']; + } + + _setValue (key, value) { + if (this._namesReserved.indexOf(key) == -1) { + this[key] = value; + } + } + + _remove (key, value) { + if (this._namesReserved.indexOf(key) == -1) { + delete this[key]; + } + } + + _getValues () { + let values = new Object(); + for (let value in this) { + if (this._namesReserved.indexOf(value) == -1) { + values[value] = this[value]; + } + } + + return values; + } +} + + +module.exports = Session; \ No newline at end of file diff --git a/components/types.js b/components/types.js index 44f8eb9..e7f3cf1 100644 --- a/components/types.js +++ b/components/types.js @@ -1,22 +1,92 @@ module.exports = { + dynamic : { + long_name : 'dynamic', + short_name : 'dyn', + checkSchema : (value, schema) => { + if (schema.values != undefined) { // values + if (schema.values.indexOf(value) == -1) { + return [false, 'valuesError']; + } + } + + return [true, 'ok']; + }, + syntax : (value, needs_convert = true) => [true, value] + }, unknown : { - long_name : 'unknown', - short_name : 'unk', - syntax : (value, needs_convert = true) => { + long_name : 'unknown', + short_name : 'unk', + checkSchema : (value, schema) => { throw new Error('Undefined datatype'); }, + syntax : (value, needs_convert = true) => { throw new Error('Undefined datatype'); } }, + integer : { + long_name : 'integer', + short_name : 'int', + checkSchema : (value, schema) => { + if (schema.values != undefined) { // values + if (schema.values.indexOf(value) == -1) { + return [false, 'valuesError']; + } + } + + if (schema.min_length != undefined) { // min_length + if (value < schema.min_length) { + return [false, 'minLengthError']; + } + } + + if (schema.max_length != undefined) { // max_length + if (value > schema.max_length) { + return [false, 'maxLengthError']; + } + } + + return [true, 'ok']; + }, + syntax : (value, needs_convert = false) => { + function isInt (value) { + if (String(parseInt(value)) == 'NaN') return false; + return String(parseInt(value)) == String(Number(value)); + } + + if (typeof(value) == 'number' & isInt(value)) { + return [true, value]; + } + else if (needs_convert & isInt(value)) { + return [true, parseInt(value)]; + } + else { + return [false, undefined]; + } + } + }, string : { long_name : 'string', short_name : 'str', checkSchema : (value, schema) => { - if (schema.values != undefined) { + if (schema.values != undefined) { // values if (schema.values.indexOf(value) == -1) { return [false, 'valuesError']; } } + + if (schema.min_length != undefined) { // min_length + if (value.length < schema.min_length) { + return [false, 'minLengthError']; + } + } + + if (schema.max_length != undefined) { // max_length + if (value.length > schema.max_length) { + return [false, 'maxLengthError']; + } + } + + return [true, 'ok']; }, - syntax : (value, needs_convert = false) => { + syntax : (value, needs_convert = false) => { if (typeof(value) == 'string') { return [true, value]; }