From 6c923aa12d9a13bc38d120f17ad91b1cc62c606c Mon Sep 17 00:00:00 2001 From: Pranav C Date: Mon, 11 Oct 2021 15:35:17 +0530 Subject: [PATCH] style(nocodb): applied prettier styling Signed-off-by: Pranav C --- packages/nocodb/.prettierrc.js | 3 +- packages/nocodb/src/__tests__/formula.test.ts | 137 +- packages/nocodb/src/__tests__/graphql.test.ts | 479 ++-- .../__tests__/noco/NcConfigFactory.test.ts | 37 +- packages/nocodb/src/__tests__/rest.test.ts | 789 +++--- packages/nocodb/src/example/docker.ts | 16 +- packages/nocodb/src/example/try.ts | 38 +- .../nocodb/src/interface/IEmailAdapter.ts | 19 +- .../nocodb/src/interface/IStorageAdapter.ts | 15 +- .../nocodb/src/interface/XcDynamicChanges.ts | 5 +- packages/nocodb/src/interface/XcMetaMgr.ts | 6 +- packages/nocodb/src/interface/config.ts | 163 +- packages/nocodb/src/lib/dataMapper/index.ts | 8 +- .../src/lib/dataMapper/lib/BaseModel.ts | 670 +++-- .../src/lib/dataMapper/lib/DbFactory.ts | 12 +- .../src/lib/dataMapper/lib/sql/CustomKnex.ts | 794 ++++-- .../lib/sql/formulaQueryBuilderFromString.ts | 103 +- .../lib/sql/functionMappings/commonFns.ts | 80 +- .../lib/sql/functionMappings/mssql.ts | 63 +- .../lib/sql/functionMappings/mysql.ts | 41 +- .../dataMapper/lib/sql/functionMappings/pg.ts | 29 +- .../lib/sql/functionMappings/sqlite.ts | 47 +- .../lib/dataMapper/lib/sql/genRollupSelect.ts | 29 +- .../lib/dataMapper/lib/sql/mapFunctionName.ts | 34 +- packages/nocodb/src/lib/index.ts | 9 +- .../migrator/SqlMigrator/lib/KnexMigrator.ts | 660 +++-- .../migrator/SqlMigrator/lib/SqlMigrator.ts | 5 +- .../SqlMigrator/lib/SqlMigratorFactory.ts | 16 +- .../lib/templates/mssql.template.ts | 42 +- .../lib/templates/mysql.template.ts | 34 +- .../SqlMigrator/lib/templates/pg.template.ts | 34 +- .../lib/templates/sqlite.template.ts | 28 +- .../nocodb/src/lib/migrator/util/Debug.ts | 37 +- .../nocodb/src/lib/migrator/util/DebugMgr.ts | 24 +- .../src/lib/migrator/util/FileCollection.ts | 12 +- .../nocodb/src/lib/migrator/util/Result.ts | 8 +- packages/nocodb/src/lib/migrator/util/emit.ts | 4 +- .../nocodb/src/lib/migrator/util/file.help.ts | 29 +- .../nocodb/src/lib/noco/NcProjectBuilder.ts | 434 +-- .../nocodb/src/lib/noco/NcProjectBuilderEE.ts | 42 +- packages/nocodb/src/lib/noco/Noco.ts | 306 +- .../src/lib/noco/common/BaseApiBuilder.ts | 1724 ++++++++---- .../src/lib/noco/common/BaseProcedure.ts | 17 +- .../src/lib/noco/common/NcConnectionMgr.ts | 138 +- .../nocodb/src/lib/noco/common/XcAudit.ts | 19 +- packages/nocodb/src/lib/noco/common/XcCron.ts | 33 +- .../nocodb/src/lib/noco/common/XcProcedure.ts | 53 +- .../common/formSubmissionEmailTemplate.ts | 2 +- .../addErrorOnColumnDeleteInFormula.ts | 19 +- .../noco/common/helpers/jsepTreeToFormula.ts | 48 +- .../helpers/updateColumnNameInFormula.ts | 23 +- .../nocodb/src/lib/noco/gql/GqlApiBuilder.ts | 2503 +++++++++++------ .../src/lib/noco/gql/GqlAuthResolver.ts | 411 +-- .../src/lib/noco/gql/GqlBaseResolver.ts | 22 +- .../src/lib/noco/gql/GqlCommonResolvers.ts | 27 +- .../nocodb/src/lib/noco/gql/GqlMiddleware.ts | 107 +- .../src/lib/noco/gql/GqlProcedureResolver.ts | 59 +- .../nocodb/src/lib/noco/gql/GqlResolver.ts | 215 +- .../nocodb/src/lib/noco/gql/auth/schema.ts | 3 +- .../nocodb/src/lib/noco/gql/common.schema.ts | 3 +- .../noco/gql/emailTemplate/forgotPassword.ts | 2 +- .../src/lib/noco/gql/emailTemplate/verify.ts | 2 +- .../nocodb/src/lib/noco/meta/MetaAPILogger.ts | 30 +- packages/nocodb/src/lib/noco/meta/NcMetaIO.ts | 285 +- .../nocodb/src/lib/noco/meta/NcMetaIOImpl.ts | 444 +-- .../src/lib/noco/meta/NcMetaIOImplEE.ts | 12 +- .../src/lib/noco/migrations/nc_001_init.ts | 161 +- .../src/lib/noco/migrations/nc_002_add_m2m.ts | 58 +- .../noco/migrations/nc_003_add_fkn_column.ts | 14 +- packages/nocodb/src/lib/noco/nc.try.ts | 43 +- .../src/lib/noco/plugins/NcPluginMgr.ts | 111 +- .../noco/plugins/adapters/cache/XcCache.ts | 13 +- .../noco/plugins/adapters/discord/Discord.ts | 21 +- .../plugins/adapters/email/EmailFactory.ts | 12 +- .../lib/noco/plugins/adapters/email/SES.ts | 18 +- .../lib/noco/plugins/adapters/email/SMTP.ts | 27 +- .../plugins/adapters/mattermost/Mattermost.ts | 21 +- .../lib/noco/plugins/adapters/slack/Slack.ts | 19 +- .../noco/plugins/adapters/storage/Local.ts | 24 +- .../noco/plugins/adapters/twilio/Twilio.ts | 20 +- packages/nocodb/src/lib/noco/plugins/azure.ts | 84 +- packages/nocodb/src/lib/noco/plugins/brand.ts | 35 +- packages/nocodb/src/lib/noco/plugins/cache.ts | 69 +- .../nocodb/src/lib/noco/plugins/discord.ts | 72 +- packages/nocodb/src/lib/noco/plugins/ee.ts | 54 +- .../nocodb/src/lib/noco/plugins/githubAuth.ts | 81 +- .../nocodb/src/lib/noco/plugins/googleAuth.ts | 81 +- .../nocodb/src/lib/noco/plugins/mattermost.ts | 72 +- packages/nocodb/src/lib/noco/plugins/ses.ts | 123 +- packages/nocodb/src/lib/noco/plugins/slack.ts | 69 +- packages/nocodb/src/lib/noco/plugins/smtp.ts | 129 +- .../nocodb/src/lib/noco/plugins/twilio.ts | 85 +- .../src/lib/noco/rest/RestApiBuilder.ts | 2248 ++++++++++----- .../nocodb/src/lib/noco/rest/RestAuthCtrl.ts | 1390 +++++---- .../src/lib/noco/rest/RestAuthCtrlEE.ts | 233 +- .../nocodb/src/lib/noco/rest/RestBaseCtrl.ts | 89 +- packages/nocodb/src/lib/noco/rest/RestCtrl.ts | 156 +- .../src/lib/noco/rest/RestCtrlBelongsTo.ts | 141 +- .../src/lib/noco/rest/RestCtrlCustom.ts | 33 +- .../src/lib/noco/rest/RestCtrlHasMany.ts | 146 +- .../nocodb/src/lib/noco/rest/RestCtrlMin.ts | 37 +- .../src/lib/noco/rest/RestCtrlProcedure.ts | 194 +- .../src/lib/noco/rest/ui/auth/emailVerify.ts | 3 +- .../lib/noco/rest/ui/auth/resetPassword.ts | 3 +- .../src/lib/noco/rest/ui/auth/signin.ts | 5 +- .../src/lib/noco/rest/ui/auth/signup.ts | 3 +- .../src/lib/noco/rest/ui/auth/swagger.ts | 3 +- .../rest/ui/emailTemplates/forgotPassword.ts | 2 +- .../lib/noco/rest/ui/emailTemplates/invite.ts | 2 +- .../lib/noco/rest/ui/emailTemplates/verify.ts | 2 +- .../src/lib/noco/upgrader/NcUpgrader.ts | 65 +- .../upgrader/jobs/ncProjectEnvUpgrader.ts | 26 +- packages/nocodb/src/lib/sqlMgr/ProjectMgr.ts | 7 +- packages/nocodb/src/lib/sqlMgr/SqlMgr.ts | 446 ++- .../nocodb/src/lib/sqlMgr/code/BaseRender.ts | 19 +- .../xc-ts/ExpressXcTsPolicyGql.ts | 191 +- .../gql-schema/xc-ts/BaseGqlXcTsSchema.ts | 119 +- .../gql-schema/xc-ts/GqlXcSchemaFactory.ts | 33 +- .../gql-schema/xc-ts/GqlXcTsSchemaMssql.ts | 47 +- .../gql-schema/xc-ts/GqlXcTsSchemaMysql.ts | 133 +- .../gql-schema/xc-ts/GqlXcTsSchemaOracle.ts | 107 +- .../code/gql-schema/xc-ts/GqlXcTsSchemaPg.ts | 276 +- .../gql-schema/xc-ts/GqlXcTsSchemaSqlite.ts | 43 +- .../code/gql-schema/xc-ts/schemaHelp.ts | 9 +- .../sqlMgr/code/models/xc/BaseModelXcMeta.ts | 12 +- .../code/models/xc/ModelXcMetaFactory.ts | 33 +- .../sqlMgr/code/models/xc/ModelXcMetaMssql.ts | 269 +- .../sqlMgr/code/models/xc/ModelXcMetaMysql.ts | 410 ++- .../code/models/xc/ModelXcMetaOracle.ts | 308 +- .../sqlMgr/code/models/xc/ModelXcMetaPg.ts | 746 +++-- .../code/models/xc/ModelXcMetaSqlite.ts | 323 +-- .../code/policies/xc/ExpressXcPolicy.ts | 107 +- .../sqlMgr/code/routers/xc-ts/SwaggerTypes.ts | 378 ++- .../sqlMgr/code/routers/xc-ts/SwaggerXc.ts | 1050 ++++--- .../sqlMgr/code/routers/xc-ts/SwaggerXcBt.ts | 103 +- .../sqlMgr/code/routers/xc-ts/SwaggerXcHm.ts | 656 ++--- .../code/routes/xc-ts/ExpressXcTsRoutes.ts | 171 +- .../code/routes/xc-ts/ExpressXcTsRoutesBt.ts | 96 +- .../code/routes/xc-ts/ExpressXcTsRoutesHm.ts | 70 +- packages/nocodb/src/lib/utils/Lang.ts | 28 +- .../nocodb/src/lib/utils/NcConfigFactory.ts | 400 +-- packages/nocodb/src/lib/utils/NcHelp.ts | 30 +- packages/nocodb/src/lib/utils/mimeTypes.ts | 1423 +++++----- packages/nocodb/src/lib/utils/projectAcl.ts | 78 +- .../nocodb/src/plugins/backblaze/Backblaze.ts | 29 +- .../src/plugins/backblaze/BackblazePlugin.ts | 9 +- .../nocodb/src/plugins/backblaze/index.ts | 108 +- .../nocodb/src/plugins/discord/Discord.ts | 17 +- .../src/plugins/discord/DiscordPlugin.ts | 12 +- packages/nocodb/src/plugins/discord/index.ts | 80 +- packages/nocodb/src/plugins/gcs/Gcs.ts | 43 +- packages/nocodb/src/plugins/gcs/GcsPlugin.ts | 9 +- packages/nocodb/src/plugins/gcs/index.ts | 95 +- .../src/plugins/linode/LinodeObjectStorage.ts | 27 +- .../linode/LinodeObjectStoragePlugin.ts | 9 +- packages/nocodb/src/plugins/linode/index.ts | 104 +- .../src/plugins/mailerSend/MailerSend.ts | 27 +- .../plugins/mailerSend/MailerSendPlugin.ts | 9 +- .../nocodb/src/plugins/mailerSend/index.ts | 86 +- .../src/plugins/mattermost/Mattermost.ts | 14 +- .../plugins/mattermost/MattermostPlugin.ts | 12 +- .../nocodb/src/plugins/mattermost/index.ts | 80 +- .../nocodb/src/plugins/mino/MinioPlugin.ts | 9 +- packages/nocodb/src/plugins/mino/index.ts | 132 +- .../nocodb/src/plugins/ovhCloud/OvhCloud.ts | 29 +- .../src/plugins/ovhCloud/OvhCloudPlugin.ts | 9 +- packages/nocodb/src/plugins/ovhCloud/index.ts | 104 +- packages/nocodb/src/plugins/s3/S3.ts | 27 +- packages/nocodb/src/plugins/s3/S3Plugin.ts | 9 +- packages/nocodb/src/plugins/s3/index.ts | 104 +- .../plugins/scaleway/ScalewayObjectStorage.ts | 25 +- .../scaleway/ScalewayObjectStoragePlugin.ts | 16 +- packages/nocodb/src/plugins/scaleway/index.ts | 104 +- packages/nocodb/src/plugins/slack/Slack.ts | 14 +- .../nocodb/src/plugins/slack/SlackPlugin.ts | 12 +- packages/nocodb/src/plugins/slack/index.ts | 80 +- packages/nocodb/src/plugins/smtp/SMTP.ts | 35 +- .../nocodb/src/plugins/smtp/SMTPPlugin.ts | 9 +- packages/nocodb/src/plugins/smtp/index.ts | 136 +- packages/nocodb/src/plugins/spaces/Spaces.ts | 28 +- .../nocodb/src/plugins/spaces/SpacesPlugin.ts | 9 +- packages/nocodb/src/plugins/spaces/index.ts | 106 +- packages/nocodb/src/plugins/teams/Teams.ts | 14 +- .../nocodb/src/plugins/teams/TeamsPlugin.ts | 12 +- packages/nocodb/src/plugins/teams/index.ts | 80 +- packages/nocodb/src/plugins/twilio/Twilio.ts | 21 +- .../nocodb/src/plugins/twilio/TwilioPlugin.ts | 12 +- packages/nocodb/src/plugins/twilio/index.ts | 93 +- .../plugins/twilioWhatsapp/TwilioWhatsapp.ts | 21 +- .../twilioWhatsapp/TwilioWhatsappPlugin.ts | 12 +- .../src/plugins/twilioWhatsapp/index.ts | 89 +- .../src/plugins/upcloud/UpCloudPlugin.ts | 9 +- .../nocodb/src/plugins/upcloud/UpoCloud.ts | 25 +- packages/nocodb/src/plugins/upcloud/index.ts | 75 +- packages/nocodb/src/plugins/vultr/Vultr.ts | 29 +- .../nocodb/src/plugins/vultr/VultrPlugin.ts | 9 +- packages/nocodb/src/plugins/vultr/index.ts | 73 +- packages/nocodb/src/types/global.d.ts | 5 +- 198 files changed, 15912 insertions(+), 12394 deletions(-) diff --git a/packages/nocodb/.prettierrc.js b/packages/nocodb/.prettierrc.js index 1de116cae7..3a429aa560 100644 --- a/packages/nocodb/.prettierrc.js +++ b/packages/nocodb/.prettierrc.js @@ -2,5 +2,6 @@ module.exports = { semi: true, trailingComma: "all", singleQuote: true, - tabWidth: 2 + tabWidth: 2, + printWidth: 120 }; diff --git a/packages/nocodb/src/__tests__/formula.test.ts b/packages/nocodb/src/__tests__/formula.test.ts index 0752ef14e7..f9d0a50c4c 100644 --- a/packages/nocodb/src/__tests__/formula.test.ts +++ b/packages/nocodb/src/__tests__/formula.test.ts @@ -1,59 +1,126 @@ -import {expect} from 'chai'; +import { expect } from 'chai'; import 'mocha'; import knex from '../lib/dataMapper/lib/sql/CustomKnex'; -import formulaQueryBuilderFromString from "../lib/dataMapper/lib/sql/formulaQueryBuilderFromString"; +import formulaQueryBuilderFromString from '../lib/dataMapper/lib/sql/formulaQueryBuilderFromString'; process.env.TEST = 'test'; describe('{Auth, CRUD, HasMany, Belongs} Tests', () => { - let knexMysqlRef; let knexPgRef; let knexMssqlRef; let knexSqliteRef; - // Called once before any of the tests in this block begin. - before(function (done) { - knexMysqlRef = knex({client:'mysql2'}) - knexMssqlRef = knex({client:'mssql'}) - knexPgRef = knex({client:'pg'}) - knexSqliteRef = knex({client:'sqlite3'}) - done() + before(function(done) { + knexMysqlRef = knex({ client: 'mysql2' }); + knexMssqlRef = knex({ client: 'mssql' }); + knexPgRef = knex({ client: 'pg' }); + knexSqliteRef = knex({ client: 'sqlite3' }); + done(); }); - - after((done) => { + after(done => { done(); }); - - describe('Formulas', function () { - - it('Simple formula', function (done) { - expect(formulaQueryBuilderFromString("concat(city, ' _ ',city_id+country_id)", 'a',knexMysqlRef).toQuery()).eq('concat(`city`,\' _ \',`city_id` + `country_id`) as a') - expect(formulaQueryBuilderFromString("concat(city, ' _ ',city_id+country_id)", 'a',knexPgRef).toQuery()).eq('concat("city",\' _ \',"city_id" + "country_id") as a') - expect(formulaQueryBuilderFromString("concat(city, ' _ ',city_id+country_id)", 'a',knexMssqlRef).toQuery()).eq('concat([city],\' _ \',[city_id] + [country_id]) as a') - expect(formulaQueryBuilderFromString("concat(city, ' _ ',city_id+country_id)", 'a',knexSqliteRef).toQuery()).eq('`city` || \' _ \' || (`city_id` + `country_id`) as a') - done() + describe('Formulas', function() { + it('Simple formula', function(done) { + expect( + formulaQueryBuilderFromString( + "concat(city, ' _ ',city_id+country_id)", + 'a', + knexMysqlRef + ).toQuery() + ).eq("concat(`city`,' _ ',`city_id` + `country_id`) as a"); + expect( + formulaQueryBuilderFromString( + "concat(city, ' _ ',city_id+country_id)", + 'a', + knexPgRef + ).toQuery() + ).eq('concat("city",\' _ \',"city_id" + "country_id") as a'); + expect( + formulaQueryBuilderFromString( + "concat(city, ' _ ',city_id+country_id)", + 'a', + knexMssqlRef + ).toQuery() + ).eq("concat([city],' _ ',[city_id] + [country_id]) as a"); + expect( + formulaQueryBuilderFromString( + "concat(city, ' _ ',city_id+country_id)", + 'a', + knexSqliteRef + ).toQuery() + ).eq("`city` || ' _ ' || (`city_id` + `country_id`) as a"); + done(); }); - it('Addition', function (done) { - expect(formulaQueryBuilderFromString("ADD(city_id,country_id,2,3,4,5,4)", 'a',knexMysqlRef).toQuery()).eq('`city_id` + `country_id` + 2 + 3 + 4 + 5 + 4 as a') - expect(formulaQueryBuilderFromString("ADD(city_id,country_id,2,3,4,5,4)", 'a',knexPgRef).toQuery()).eq('"city_id" + "country_id" + 2 + 3 + 4 + 5 + 4 as a') - expect(formulaQueryBuilderFromString("ADD(city_id,country_id,2,3,4,5,4)", 'a',knexMssqlRef).toQuery()).eq('[city_id] + [country_id] + 2 + 3 + 4 + 5 + 4 as a') - expect(formulaQueryBuilderFromString("ADD(city_id,country_id,2,3,4,5,4)", 'a',knexSqliteRef).toQuery()).eq('`city_id` + `country_id` + 2 + 3 + 4 + 5 + 4 as a') - done() + it('Addition', function(done) { + expect( + formulaQueryBuilderFromString( + 'ADD(city_id,country_id,2,3,4,5,4)', + 'a', + knexMysqlRef + ).toQuery() + ).eq('`city_id` + `country_id` + 2 + 3 + 4 + 5 + 4 as a'); + expect( + formulaQueryBuilderFromString( + 'ADD(city_id,country_id,2,3,4,5,4)', + 'a', + knexPgRef + ).toQuery() + ).eq('"city_id" + "country_id" + 2 + 3 + 4 + 5 + 4 as a'); + expect( + formulaQueryBuilderFromString( + 'ADD(city_id,country_id,2,3,4,5,4)', + 'a', + knexMssqlRef + ).toQuery() + ).eq('[city_id] + [country_id] + 2 + 3 + 4 + 5 + 4 as a'); + expect( + formulaQueryBuilderFromString( + 'ADD(city_id,country_id,2,3,4,5,4)', + 'a', + knexSqliteRef + ).toQuery() + ).eq('`city_id` + `country_id` + 2 + 3 + 4 + 5 + 4 as a'); + done(); }); - it('Average', function (done) { - expect(formulaQueryBuilderFromString("AVG(city_id,country_id,2,3,4,5,4)", 'a',knexMysqlRef).toQuery()).eq('(`city_id` + `country_id` + 2 + 3 + 4 + 5 + 4) / 7 as a') - expect(formulaQueryBuilderFromString("AVG(city_id,country_id,2,3,4,5,4)", 'a',knexPgRef).toQuery()).eq('("city_id" + "country_id" + 2 + 3 + 4 + 5 + 4) / 7 as a') - expect(formulaQueryBuilderFromString("AVG(city_id,country_id,2,3,4,5,4)", 'a',knexMssqlRef).toQuery()).eq('([city_id] + [country_id] + 2 + 3 + 4 + 5 + 4) / 7 as a') - expect(formulaQueryBuilderFromString("AVG(city_id,country_id,2,3,4,5,4)", 'a',knexSqliteRef).toQuery()).eq('(`city_id` + `country_id` + 2 + 3 + 4 + 5 + 4) / 7 as a') - done() + it('Average', function(done) { + expect( + formulaQueryBuilderFromString( + 'AVG(city_id,country_id,2,3,4,5,4)', + 'a', + knexMysqlRef + ).toQuery() + ).eq('(`city_id` + `country_id` + 2 + 3 + 4 + 5 + 4) / 7 as a'); + expect( + formulaQueryBuilderFromString( + 'AVG(city_id,country_id,2,3,4,5,4)', + 'a', + knexPgRef + ).toQuery() + ).eq('("city_id" + "country_id" + 2 + 3 + 4 + 5 + 4) / 7 as a'); + expect( + formulaQueryBuilderFromString( + 'AVG(city_id,country_id,2,3,4,5,4)', + 'a', + knexMssqlRef + ).toQuery() + ).eq('([city_id] + [country_id] + 2 + 3 + 4 + 5 + 4) / 7 as a'); + expect( + formulaQueryBuilderFromString( + 'AVG(city_id,country_id,2,3,4,5,4)', + 'a', + knexSqliteRef + ).toQuery() + ).eq('(`city_id` + `country_id` + 2 + 3 + 4 + 5 + 4) / 7 as a'); + done(); }); }); - -});/** +}); +/** * @copyright Copyright (c) 2021, Xgene Cloud Ltd * * @author Naveen MR diff --git a/packages/nocodb/src/__tests__/graphql.test.ts b/packages/nocodb/src/__tests__/graphql.test.ts index dd19f82ce2..e594e5ff00 100644 --- a/packages/nocodb/src/__tests__/graphql.test.ts +++ b/packages/nocodb/src/__tests__/graphql.test.ts @@ -1,76 +1,77 @@ -import {expect} from 'chai'; +import { expect } from 'chai'; import 'mocha'; import express from 'express'; import request from 'supertest'; -import {Noco} from "../lib"; -import NcConfigFactory from "../lib/utils/NcConfigFactory"; +import { Noco } from '../lib'; +import NcConfigFactory from '../lib/utils/NcConfigFactory'; process.env.TEST = 'test'; - -const dbConfig = NcConfigFactory.urlToDbConfig(NcConfigFactory.extractXcUrlFromJdbc(process.env[`DATABASE_URL`]), null, null, 'graphql'); +const dbConfig = NcConfigFactory.urlToDbConfig( + NcConfigFactory.extractXcUrlFromJdbc(process.env[`DATABASE_URL`]), + null, + null, + 'graphql' +); const projectCreateReqBody = { - "api": "projectCreateByWeb", - "query": {"skipProjectHasDb": 1}, - "args": { - "project": {"title": "sebulba", "folder": "config.xc.json", "type": "pg"}, - "projectJson": { - "title": "sebulba", - "version": "0.6", - "envs": { - "_noco": { - "db": [ - dbConfig - ], "apiClient": {"data": []} + api: 'projectCreateByWeb', + query: { skipProjectHasDb: 1 }, + args: { + project: { title: 'sebulba', folder: 'config.xc.json', type: 'pg' }, + projectJson: { + title: 'sebulba', + version: '0.6', + envs: { + _noco: { + db: [dbConfig], + apiClient: { data: [] } } }, - "workingEnv": "_noco", - "meta": { - "version": "0.6", - "seedsFolder": "seeds", - "queriesFolder": "queries", - "apisFolder": "apis", - "projectType": "graphql", - "type": "mvc", - "language": "ts", - "db": {"client": "sqlite3", "connection": {"filename": "noco.db"}} + workingEnv: '_noco', + meta: { + version: '0.6', + seedsFolder: 'seeds', + queriesFolder: 'queries', + apisFolder: 'apis', + projectType: 'graphql', + type: 'mvc', + language: 'ts', + db: { client: 'sqlite3', connection: { filename: 'noco.db' } } }, - "seedsFolder": "seeds", - "queriesFolder": "queries", - "apisFolder": "apis", - "projectType": "graphql", - "type": "docker", - "language": "ts", - "apiClient": {"data": []}, - "auth": {"jwt": {"secret": "b8ed266d-4475-4028-8c3d-590f58bee867", "dbAlias": "db"}} + seedsFolder: 'seeds', + queriesFolder: 'queries', + apisFolder: 'apis', + projectType: 'graphql', + type: 'docker', + language: 'ts', + apiClient: { data: [] }, + auth: { + jwt: { secret: 'b8ed266d-4475-4028-8c3d-590f58bee867', dbAlias: 'db' } + } } } -} - +}; describe('{Auth, CRUD, HasMany, Belongs} Tests', () => { - let app; let token; let projectId; - // Called once before any of the tests in this block begin. - before(function (done) { + before(function(done) { this.timeout(10000); (async () => { - const server = express(); server.use(await Noco.init({})); app = server; - - })().then(done).catch(done); + })() + .then(done) + .catch(done); }); - - after((done) => { + after(done => { done(); // process.exit(); }); @@ -233,39 +234,39 @@ describe('{Auth, CRUD, HasMany, Belongs} Tests', () => { // /**** Authentication : END ****/ /**************** START : Auth ****************/ - describe('Authentication', function () { + describe('Authentication', function() { this.timeout(10000); - const EMAIL_ID = 'abc@g.com' + const EMAIL_ID = 'abc@g.com'; const VALID_PASSWORD = '1234566778'; - it('Signup with valid email', function (done) { - this.timeout(20000) + it('Signup with valid email', function(done) { + this.timeout(20000); request(app) .post('/auth/signup') - .send({email: EMAIL_ID, password: VALID_PASSWORD}) + .send({ email: EMAIL_ID, password: VALID_PASSWORD }) .expect(200, (err, res) => { if (err) { - expect(res.status).to.equal(400) + expect(res.status).to.equal(400); } else { const token = res.body.token; - expect(token).to.be.a("string") + expect(token).to.be.a('string'); } done(); }); }); - it('Signup with invalid email', (done) => { + it('Signup with invalid email', done => { request(app) .post('/auth/signup') - .send({email: 'test', password: VALID_PASSWORD}) + .send({ email: 'test', password: VALID_PASSWORD }) .expect(400, done); }); - it('Signin with valid credentials', function (done) { + it('Signin with valid credentials', function(done) { request(app) .post('/auth/signin') - .send({email: EMAIL_ID, password: VALID_PASSWORD}) - .expect(200, async function (err, res) { + .send({ email: EMAIL_ID, password: VALID_PASSWORD }) + .expect(200, async function(err, res) { if (err) { return done(err); } @@ -279,11 +280,11 @@ describe('{Auth, CRUD, HasMany, Belongs} Tests', () => { }); }); - it('me', function (done) { + it('me', function(done) { request(app) .get('/user/me') .set('xc-auth', token) - .expect(200, function (err, res) { + .expect(200, function(err, res) { if (err) { return done(err); } @@ -293,68 +294,62 @@ describe('{Auth, CRUD, HasMany, Belongs} Tests', () => { }); }); - - it('Change password', function (done) { + it('Change password', function(done) { request(app) .post('/user/password/change') .set('xc-auth', token) - .send({currentPassword: 'password', newPassword: 'password'}) + .send({ currentPassword: 'password', newPassword: 'password' }) .expect(400, done); }); - - it('Change password - after logout', function (done) { + it('Change password - after logout', function(done) { // todo: request(app) .post('/user/password/change') - .send({currentPassword: 'password', newPassword: 'password'}) - .expect(500, function (_err, _res) { - done() + .send({ currentPassword: 'password', newPassword: 'password' }) + .expect(500, function(_err, _res) { + done(); }); }); - - it('Signin with invalid credentials', function (done) { + it('Signin with invalid credentials', function(done) { request(app) .post('/auth/signin') - .send({email: 'abc@abc.com', password: VALID_PASSWORD}) + .send({ email: 'abc@abc.com', password: VALID_PASSWORD }) .expect(400, done); }); - it('Signin with invalid password', function (done) { + it('Signin with invalid password', function(done) { request(app) .post('/auth/signin') - .send({email: EMAIL_ID, password: 'wrongPassword'}) + .send({ email: EMAIL_ID, password: 'wrongPassword' }) .expect(400, done); }); - - it('Forgot password with a non-existing email id', function (done) { + it('Forgot password with a non-existing email id', function(done) { request(app) .post('/auth/password/forgot') - .send({email: 'abc@abc.com'}) + .send({ email: 'abc@abc.com' }) .expect(400, done); }); - it('Forgot password with an existing email id', function (done) { - this.timeout(10000) + it('Forgot password with an existing email id', function(done) { + this.timeout(10000); request(app) .post('/auth/password/forgot') - .send({email: EMAIL_ID}) + .send({ email: EMAIL_ID }) .expect(200, done); }); - it('Email validate with an invalid token', function (done) { + it('Email validate with an invalid token', function(done) { request(app) .post('/auth/email/validate/someRandomValue') - .send({email: EMAIL_ID}) + .send({ email: EMAIL_ID }) .expect(400, done); }); - it('Email validate with a valid token', function (done) { - - console.log('eeee') - + it('Email validate with a valid token', function(done) { + console.log('eeee'); // todo : done(); @@ -365,18 +360,17 @@ describe('{Auth, CRUD, HasMany, Belongs} Tests', () => { // .expect(500, done); }); - - it('Forgot password validate with an invalid token', function (done) { + it('Forgot password validate with an invalid token', function(done) { request(app) .post('/auth/token/validate/someRandomValue') - .send({email: EMAIL_ID}) + .send({ email: EMAIL_ID }) .expect(400, done); }); - it('Forgot password validate with a valid token', function (done) { + it('Forgot password validate with a valid token', function(done) { // todo - done() + done(); // request(app) // .post('/auth/token/validate/someRandomValue') @@ -384,38 +378,34 @@ describe('{Auth, CRUD, HasMany, Belongs} Tests', () => { // .expect(500, done); }); - - it('Reset Password with an invalid token', function (done) { + it('Reset Password with an invalid token', function(done) { request(app) .post('/auth/password/reset/someRandomValue') - .send({password: 'anewpassword'}) + .send({ password: 'anewpassword' }) .expect(400, done); }); - it('Reset Password with an valid token', function (done) { + it('Reset Password with an valid token', function(done) { //todo - done() + done(); // request(app) // .post('/auth/password/reset/someRandomValue') // .send({password: 'anewpassword'}) // .expect(500, done); }); - - }); - describe('Project', function () { - const EMAIL_ID = 'abc@g.com' + describe('Project', function() { + const EMAIL_ID = 'abc@g.com'; const VALID_PASSWORD = '1234566778'; - - before(function (done) { - this.timeout(120000) + before(function(done) { + this.timeout(120000); request(app) .post('/auth/signin') - .send({email: EMAIL_ID, password: VALID_PASSWORD}) - .expect(200, async function (_err, res) { + .send({ email: EMAIL_ID, password: VALID_PASSWORD }) + .expect(200, async function(_err, res) { token = res.body.token; request(app) .post('/dashboard') @@ -423,34 +413,33 @@ describe('{Auth, CRUD, HasMany, Belongs} Tests', () => { .send(projectCreateReqBody) .expect(200, (err, res) => { if (err) { - return done(err) + return done(err); } projectId = res.body.id; done(); - }) + }); }); - }) + }); /**** country : START ****/ - describe('country', function () { - + describe('country', function() { /**** Query : START ****/ - it('countryList', function (done) { + it('countryList', function(done) { request(app) .post(`/nc/${projectId}/v1/graphql`) .set('xc-auth', token) .send({ query: `{ countryList(limit:5){ country_id country } }` }) - .expect(200, function (err, res) { + .expect(200, function(err, res) { if (err) done(err); const list = res.body.data.countryList; - expect(list).length.to.be.most(5) - expect(list[0]).to.have.all.keys(['country_id', 'country']) - done() - }) + expect(list).length.to.be.most(5); + expect(list[0]).to.have.all.keys(['country_id', 'country']); + done(); + }); }); - it('countryList - with sort', function (done) { + it('countryList - with sort', function(done) { // todo: order -> sort request(app) @@ -460,24 +449,24 @@ describe('{Auth, CRUD, HasMany, Belongs} Tests', () => { .send({ query: `{ countryList(sort:"-country_id"){ country_id country } }` }) - .expect(200, function (err, res) { + .expect(200, function(err, res) { if (err) done(err); const list = res.body.data.countryList; - expect(list[0]).to.have.all.keys(['country_id', 'country']) + expect(list[0]).to.have.all.keys(['country_id', 'country']); expect(list).satisfy(array => { let i = array.length; while (--i) { if (array[i].country_id > array[i - 1].country_id) return false; } - return true - }, 'Should be in descending order') + return true; + }, 'Should be in descending order'); - done() - }) + done(); + }); }); - it('countryList - with limit', function (done) { + it('countryList - with limit', function(done) { request(app) .post(`/nc/${projectId}/v1/graphql`) @@ -485,16 +474,16 @@ describe('{Auth, CRUD, HasMany, Belongs} Tests', () => { .send({ query: `{ countryList(limit:6){ country_id country } }` }) - .expect(200, function (err, res) { + .expect(200, function(err, res) { if (err) done(err); const list = res.body.data.countryList; - expect(list[0]).to.have.all.keys(['country_id', 'country']) - expect(list).to.have.length.most(6) - done() - }) + expect(list[0]).to.have.all.keys(['country_id', 'country']); + expect(list).to.have.length.most(6); + done(); + }); }); - it('countryList - with offset', function (done) { + it('countryList - with offset', function(done) { request(app) .post(`/nc/${projectId}/v1/graphql`) @@ -502,10 +491,10 @@ describe('{Auth, CRUD, HasMany, Belongs} Tests', () => { .send({ query: `{ countryList(offset:0,limit:6){ country_id country } }` }) - .expect(200, function (err, res) { + .expect(200, function(err, res) { if (err) done(err); const list1 = res.body.data.countryList; - expect(list1[0]).to.have.all.keys(['country_id', 'country']) + expect(list1[0]).to.have.all.keys(['country_id', 'country']); request(app) .post(`/nc/${projectId}/v1/graphql`) @@ -513,20 +502,26 @@ describe('{Auth, CRUD, HasMany, Belongs} Tests', () => { .send({ query: `{ countryList(offset:1,limit:5){ country_id country } }` }) - .expect(200, function (err, res1) { + .expect(200, function(err, res1) { if (err) done(err); const list2 = res1.body.data.countryList; - expect(list2[0]).to.have.all.keys(['country_id', 'country']) - expect(list2).satisfy(arr => arr.every(({country, country_id}, i) => - country === list1[i + 1].country && country_id === list1[i + 1].country_id - ), 'Both data should need to be equal where offset vary with 1') - - done() + expect(list2[0]).to.have.all.keys(['country_id', 'country']); + expect(list2).satisfy( + arr => + arr.every( + ({ country, country_id }, i) => + country === list1[i + 1].country && + country_id === list1[i + 1].country_id + ), + 'Both data should need to be equal where offset vary with 1' + ); + + done(); }); - }) + }); }); - it('countryList - nested count', function (done) { + it('countryList - nested count', function(done) { request(app) .post(`/nc/${projectId}/v1/graphql`) @@ -534,17 +529,21 @@ describe('{Auth, CRUD, HasMany, Belongs} Tests', () => { .send({ query: `{ countryList{ country_id country cityCount} }` }) - .expect(200, function (err, res) { + .expect(200, function(err, res) { if (err) done(err); const list = res.body.data.countryList; - expect(list[0]).to.have.all.keys(['country_id', 'country', 'cityCount']) - expect(list[0].cityCount).to.be.a('number') + expect(list[0]).to.have.all.keys([ + 'country_id', + 'country', + 'cityCount' + ]); + expect(list[0].cityCount).to.be.a('number'); expect(list[0].cityCount % 1).to.be.equal(0); - done() - }) + done(); + }); }); - it('countryList - nested cityList', function (done) { + it('countryList - nested cityList', function(done) { request(app) .post(`/nc/${projectId}/v1/graphql`) @@ -552,22 +551,31 @@ describe('{Auth, CRUD, HasMany, Belongs} Tests', () => { .send({ query: `{ countryList{ country_id country cityList { city country_id }} }` }) - .expect(200, function (err, res) { + .expect(200, function(err, res) { if (err) done(err); const list = res.body.data.countryList; - expect(list[0]).to.have.all.keys(['country_id', 'country', 'cityList']) - expect(list[0].cityList).to.be.a('Array') + expect(list[0]).to.have.all.keys([ + 'country_id', + 'country', + 'cityList' + ]); + expect(list[0].cityList).to.be.a('Array'); if (dbConfig.client !== 'mssql') { expect(list[0].cityList[0]).to.be.a('object'); - expect(list[0].cityList[0]).to.have.all.keys(['country_id', 'city']) - expect(Object.keys(list[0].cityList[0])).to.have.length(2) - expect(list[0].cityList[0].country_id).to.be.equal(list[0].country_id) + expect(list[0].cityList[0]).to.have.all.keys([ + 'country_id', + 'city' + ]); + expect(Object.keys(list[0].cityList[0])).to.have.length(2); + expect(list[0].cityList[0].country_id).to.be.equal( + list[0].country_id + ); } - done() - }) + done(); + }); }); - it('countryRead', function (done) { + it('countryRead', function(done) { request(app) .post(`/nc/${projectId}/v1/graphql`) @@ -575,17 +583,17 @@ describe('{Auth, CRUD, HasMany, Belongs} Tests', () => { .send({ query: `{ countryRead(id: "1"){ country_id country } } ` }) - .expect(200, function (err, res) { + .expect(200, function(err, res) { if (err) done(err); const data = res.body.data.countryRead; - expect(data).to.be.a('object') - expect(data).to.have.all.keys(['country_id', 'country']) + expect(data).to.be.a('object'); + expect(data).to.have.all.keys(['country_id', 'country']); - done() - }) + done(); + }); }); - it('countryExists', function (done) { + it('countryExists', function(done) { request(app) .post(`/nc/${projectId}/v1/graphql`) @@ -593,16 +601,16 @@ describe('{Auth, CRUD, HasMany, Belongs} Tests', () => { .send({ query: `{ countryExists(id: "1") } ` }) - .expect(200, function (err, res) { + .expect(200, function(err, res) { if (err) done(err); const data = res.body.data.countryExists; - expect(data).to.be.a('boolean') - expect(data).to.be.equal(true) - done() - }) + expect(data).to.be.a('boolean'); + expect(data).to.be.equal(true); + done(); + }); }); - it('countryExists - with non-existing id', function (done) { + it('countryExists - with non-existing id', function(done) { request(app) .post(`/nc/${projectId}/v1/graphql`) @@ -610,16 +618,16 @@ describe('{Auth, CRUD, HasMany, Belongs} Tests', () => { .send({ query: `{ countryExists(id: "30000") } ` }) - .expect(200, function (err, res) { + .expect(200, function(err, res) { if (err) done(err); const data = res.body.data.countryExists; - expect(data).to.be.a('boolean') - expect(data).to.be.equal(false) - done() - }) + expect(data).to.be.a('boolean'); + expect(data).to.be.equal(false); + done(); + }); }); - it('countryFindOne', function (done) { + it('countryFindOne', function(done) { request(app) .post(`/nc/${projectId}/v1/graphql`) @@ -627,17 +635,17 @@ describe('{Auth, CRUD, HasMany, Belongs} Tests', () => { .send({ query: `{ countryFindOne (where: "(country_id,eq,1)"){ country country_id } } ` }) - .expect(200, function (err, res) { + .expect(200, function(err, res) { if (err) done(err); const data = res.body.data.countryFindOne; - expect(data).to.be.a('object') - expect(data).to.have.all.keys(['country', 'country_id']) + expect(data).to.be.a('object'); + expect(data).to.have.all.keys(['country', 'country_id']); expect(data.country_id).to.be.equal(1); - done() - }) + done(); + }); }); - it('countryCount - filter by id', function (done) { + it('countryCount - filter by id', function(done) { request(app) .post(`/nc/${projectId}/v1/graphql`) @@ -645,16 +653,16 @@ describe('{Auth, CRUD, HasMany, Belongs} Tests', () => { .send({ query: `{ countryCount (where: "(country_id,eq,1)") } ` }) - .expect(200, function (err, res) { + .expect(200, function(err, res) { if (err) done(err); const data = res.body.data.countryCount; - expect(data).to.be.a('number') + expect(data).to.be.a('number'); expect(data).to.be.equal(1); - done() - }) + done(); + }); }); - it('countryDistinct', function (done) { + it('countryDistinct', function(done) { request(app) .post(`/nc/${projectId}/v1/graphql`) @@ -662,19 +670,19 @@ describe('{Auth, CRUD, HasMany, Belongs} Tests', () => { .send({ query: `{ countryDistinct(column_name: "last_update") { last_update } } ` }) - .expect(200, function (err, res) { + .expect(200, function(err, res) { if (err) done(err); const data = res.body.data.countryDistinct; - expect(data).to.be.a('array') - expect(data[0]).to.be.a('object') - expect(data[0]).to.have.all.keys(['last_update']) + expect(data).to.be.a('array'); + expect(data[0]).to.be.a('object'); + expect(data[0]).to.have.all.keys(['last_update']); expect(data[0].last_update).to.be.match(/\d+/); - done() - }) + done(); + }); }); if (dbConfig.client !== 'mssql') { - it('countryGroupBy', function (done) { + it('countryGroupBy', function(done) { request(app) .post(`/nc/${projectId}/v1/graphql`) @@ -682,18 +690,18 @@ describe('{Auth, CRUD, HasMany, Belongs} Tests', () => { .send({ query: `{ countryGroupBy(fields: "last_update",limit:5) { last_update count } } ` }) - .expect(200, function (err, res) { + .expect(200, function(err, res) { if (err) done(err); const data = res.body.data.countryGroupBy; expect(data.length).to.be.most(5); expect(data[0].count).to.be.greaterThan(0); expect(data[0].last_update).to.be.a('string'); expect(Object.keys(data[0]).length).to.be.equal(2); - done() - }) + done(); + }); }); - it('countryGroupBy - Multiple', function (done) { + it('countryGroupBy - Multiple', function(done) { request(app) .post(`/nc/${projectId}/v1/graphql`) @@ -701,7 +709,7 @@ describe('{Auth, CRUD, HasMany, Belongs} Tests', () => { .send({ query: `{ countryGroupBy(fields: "last_update,country",limit:5) { last_update country count } } ` }) - .expect(200, function (err, res) { + .expect(200, function(err, res) { if (err) done(err); const data = res.body.data.countryGroupBy; expect(data.length).to.be.most(5); @@ -709,11 +717,11 @@ describe('{Auth, CRUD, HasMany, Belongs} Tests', () => { expect(data[0].last_update).to.be.a('string'); expect(data[0].country).to.be.a('string'); expect(Object.keys(data[0]).length).to.be.equal(3); - done() - }) + done(); + }); }); - it('countryAggregate', function (done) { + it('countryAggregate', function(done) { request(app) .post(`/nc/${projectId}/v1/graphql`) @@ -721,7 +729,7 @@ describe('{Auth, CRUD, HasMany, Belongs} Tests', () => { .send({ query: `{ countryAggregate(func: "sum,avg,min,max,count", column_name : "country_id") { sum avg min max count } } ` }) - .expect(200, function (err, res) { + .expect(200, function(err, res) { if (err) done(err); const data = res.body.data.countryAggregate; expect(data).to.be.a('array'); @@ -730,14 +738,19 @@ describe('{Auth, CRUD, HasMany, Belongs} Tests', () => { expect(data[0].max).to.be.a('number'); expect(data[0].avg).to.be.a('number'); expect(data[0].sum).to.be.a('number'); - expect(data[0].count).to.be.a('number').and.satisfy(num => num === parseInt(num), 'count should be an integer'); + expect(data[0].count) + .to.be.a('number') + .and.satisfy( + num => num === parseInt(num), + 'count should be an integer' + ); expect(Object.keys(data[0]).length).to.be.equal(5); } done(); - }) + }); }); - it('countryDistribution', function (done) { + it('countryDistribution', function(done) { request(app) .post(`/nc/${projectId}/v1/graphql`) @@ -745,67 +758,71 @@ describe('{Auth, CRUD, HasMany, Belongs} Tests', () => { .send({ query: `{ countryDistribution(column_name : "country_id") { range count } } ` }) - .expect(200, function (err, res) { + .expect(200, function(err, res) { if (err) done(err); const data = res.body.data.countryDistribution; expect(data).to.be.a('array'); expect(data[0].count).to.be.a('number'); - expect(data[0].count).satisfies(num => num === parseInt(num) && num >= 0, 'should be a positive integer'); + expect(data[0].count).satisfies( + num => num === parseInt(num) && num >= 0, + 'should be a positive integer' + ); expect(data[0].range).to.be.a('string'); - expect(data[0].range).to.be.match(/^\d+-\d+$/, 'should match {num start}-{num end} format') + expect(data[0].range).to.be.match( + /^\d+-\d+$/, + 'should match {num start}-{num end} format' + ); done(); - }) + }); }); } /**** Query : END ****/ /**** Mutation : START ****/ - describe('Mutation', function () { - + describe('Mutation', function() { const COUNTRY_ID = 9999; // const COUNTRY_CREATE_ID = 9998; // const COUNTRY_NAME = 'test-name'; - - before(function (done) { + before(function(done) { // create table entry for update and delete // let db = knex(config.envs.dev.db[0])('country'); // db.insert({ // country_id: COUNTRY_ID, // country: COUNTRY_NAME // }).finally(() => done()) - done() - }) - - after(function (done) { + done(); + }); + after(function(done) { // delete table entries which is created for the test // let db = knex(config.envs.dev.db[0])('country'); // db.whereIn('country_id', [COUNTRY_ID, COUNTRY_CREATE_ID]) // .del() // .finally(() => done()) - done() - }) + done(); + }); - it('countryCreate', function (done) { + it('countryCreate', function(done) { request(app) .post(`/nc/${projectId}/v1/graphql`) .set('xc-auth', token) .send({ - query: `mutation{ countryCreate( data : { country: "abcd" ${dbConfig.client === 'sqlite3' ? ' country_id : 999 ' : ''} }) { country_id country } } ` + query: `mutation{ countryCreate( data : { country: "abcd" ${ + dbConfig.client === 'sqlite3' ? ' country_id : 999 ' : '' + } }) { country_id country } } ` }) - .expect(200, function (err, res) { + .expect(200, function(err, res) { if (err) done(err); const data = res.body.data.countryCreate; expect(data).to.be.a('object'); expect(data.country_id).to.be.a('number'); expect(data.country).to.be.equal('abcd'); done(); - }) + }); }); - - it('countryUpdate', function (done) { + it('countryUpdate', function(done) { request(app) .post(`/nc/${projectId}/v1/graphql`) @@ -813,16 +830,16 @@ describe('{Auth, CRUD, HasMany, Belongs} Tests', () => { .send({ query: `mutation{ countryUpdate( id : "${COUNTRY_ID}", data : { country: "abcd" }){ country } } ` }) - .expect(200, function (err, res) { + .expect(200, function(err, res) { if (err) done(err); const data = res.body.data.countryUpdate; expect(data).to.be.a('object'); // todo: done(); - }) + }); }); - it('countryDelete', function (done) { + it('countryDelete', function(done) { request(app) .post(`/nc/${projectId}/v1/graphql`) @@ -830,16 +847,15 @@ describe('{Auth, CRUD, HasMany, Belongs} Tests', () => { .send({ query: `mutation{ countryDelete( id : "${COUNTRY_ID}") } ` }) - .expect(200, function (err, res) { + .expect(200, function(err, res) { if (err) done(err); const data = res.body.data.countryDelete; - expect(data).to.be.a('number') + expect(data).to.be.a('number'); // todo: done(); - }) + }); }); - }) - + }); /**** Mutation : END ****/ // countryCreateBulk(data: [countryInput]): [Int] @@ -849,7 +865,8 @@ describe('{Auth, CRUD, HasMany, Belongs} Tests', () => { }); /**** country : END ****/ }); -});/** +}); +/** * @copyright Copyright (c) 2021, Xgene Cloud Ltd * * @author Naveen MR diff --git a/packages/nocodb/src/__tests__/noco/NcConfigFactory.test.ts b/packages/nocodb/src/__tests__/noco/NcConfigFactory.test.ts index 51ec0b4a13..c33ddf9bc1 100644 --- a/packages/nocodb/src/__tests__/noco/NcConfigFactory.test.ts +++ b/packages/nocodb/src/__tests__/noco/NcConfigFactory.test.ts @@ -1,10 +1,8 @@ -import {expect} from 'chai'; +import { expect } from 'chai'; import 'mocha'; -import {NcConfigFactory} from "../../lib"; - +import { NcConfigFactory } from '../../lib'; describe('Config Factory Tests', () => { - const expectedObject = { client: 'pg', connection: { @@ -24,31 +22,36 @@ describe('Config Factory Tests', () => { acquireConnectionTimeout: 600000 }; - before(function (done) { + before(function(done) { done(); }); - - after((done) => { + after(done => { done(); - }) + }); - it('Generate config from string', function (done) { - const config = NcConfigFactory.metaUrlToDbConfig(`pg://localhost:5432?u=postgres&p=xgene&d=abcde`); + it('Generate config from string', function(done) { + const config = NcConfigFactory.metaUrlToDbConfig( + `pg://localhost:5432?u=postgres&p=xgene&d=abcde` + ); const { pool, ssl, ...rest } = expectedObject; expect(config).to.deep.equal(rest); done(); }); - it('Connection string with nested property', function (done) { - const config = NcConfigFactory.metaUrlToDbConfig(`pg://localhost:5432?u=postgres&p=xgene&d=abcde&pool.min=1&pool.max=2&ssl.rejectUnauthorized=false`); + it('Connection string with nested property', function(done) { + const config = NcConfigFactory.metaUrlToDbConfig( + `pg://localhost:5432?u=postgres&p=xgene&d=abcde&pool.min=1&pool.max=2&ssl.rejectUnauthorized=false` + ); expect(config).to.deep.equal(expectedObject); done(); }); it('Allow creating config from JSON string', function(done) { try { process.env.NC_DB_JSON = JSON.stringify(expectedObject); - - const { meta: { db: config } } = NcConfigFactory.make(); + + const { + meta: { db: config } + } = NcConfigFactory.make(); expect(config).to.deep.equal(expectedObject); done(); } finally { @@ -58,8 +61,10 @@ describe('Config Factory Tests', () => { it('Allow creating config from JSON file', function(done) { try { process.env.NC_DB_JSON_FILE = `${__dirname}/dbConfig.json`; - - const { meta: { db: config } } = NcConfigFactory.make(); + + const { + meta: { db: config } + } = NcConfigFactory.make(); expect(config).to.deep.equal(expectedObject); done(); } finally { diff --git a/packages/nocodb/src/__tests__/rest.test.ts b/packages/nocodb/src/__tests__/rest.test.ts index 8933c1da6c..3429d7b5a4 100644 --- a/packages/nocodb/src/__tests__/rest.test.ts +++ b/packages/nocodb/src/__tests__/rest.test.ts @@ -1,26 +1,28 @@ -import {expect} from 'chai'; +import { expect } from 'chai'; import 'mocha'; import express from 'express'; import request from 'supertest'; -import {Noco} from "../lib"; -import NcConfigFactory from "../lib/utils/NcConfigFactory"; +import { Noco } from '../lib'; +import NcConfigFactory from '../lib/utils/NcConfigFactory'; process.env.TEST = 'test'; let projectId; let token; -const dbConfig = NcConfigFactory.urlToDbConfig(NcConfigFactory.extractXcUrlFromJdbc(process.env[`DATABASE_URL`])); +const dbConfig = NcConfigFactory.urlToDbConfig( + NcConfigFactory.extractXcUrlFromJdbc(process.env[`DATABASE_URL`]) +); const projectCreateReqBody = { - "api": "projectCreateByWeb", - "query": {"skipProjectHasDb": 1}, - "args": { - "project": {"title": "sebulba", "folder": "config.xc.json", "type": "pg"}, - "projectJson": { - "title": "sebulba", - "version": "0.6", - "envs": { - "_noco": { - "db": [ + api: 'projectCreateByWeb', + query: { skipProjectHasDb: 1 }, + args: { + project: { title: 'sebulba', folder: 'config.xc.json', type: 'pg' }, + projectJson: { + title: 'sebulba', + version: '0.6', + envs: { + _noco: { + db: [ dbConfig // { // "client": "mysql2", @@ -39,96 +41,96 @@ const projectCreateReqBody = { // "inflection": {"tn": "none", "cn": "none"} // } // } - ], "apiClient": {"data": []} + ], + apiClient: { data: [] } } }, - "workingEnv": "_noco", - "meta": { - "version": "0.6", - "seedsFolder": "seeds", - "queriesFolder": "queries", - "apisFolder": "apis", - "projectType": "rest", - "type": "mvc", - "language": "ts", - "db": {"client": "sqlite3", "connection": {"filename": "noco.db"}} + workingEnv: '_noco', + meta: { + version: '0.6', + seedsFolder: 'seeds', + queriesFolder: 'queries', + apisFolder: 'apis', + projectType: 'rest', + type: 'mvc', + language: 'ts', + db: { client: 'sqlite3', connection: { filename: 'noco.db' } } }, - "seedsFolder": "seeds", - "queriesFolder": "queries", - "apisFolder": "apis", - "projectType": "rest", - "type": "docker", - "language": "ts", - "apiClient": {"data": []}, - "auth": {"jwt": {"secret": "b8ed266d-4475-4028-8c3d-590f58bee867", "dbAlias": "db"}} + seedsFolder: 'seeds', + queriesFolder: 'queries', + apisFolder: 'apis', + projectType: 'rest', + type: 'docker', + language: 'ts', + apiClient: { data: [] }, + auth: { + jwt: { secret: 'b8ed266d-4475-4028-8c3d-590f58bee867', dbAlias: 'db' } + } } } -} +}; // console.log(JSON.stringify(dbConfig, null, 2)); // process.exit(); describe('{Auth, CRUD, HasMany, Belongs} Tests', () => { - let app; - // Called once before any of the tests in this block begin. - before(function (done) { + before(function(done) { this.timeout(200000); (async () => { - const server = express(); server.use(await Noco.init()); app = server; // await knex(config.envs[process.env.NODE_ENV || 'dev'].db[0])('xc_users').del(); - })().then(done).catch((e) => { - done(e) - }); + })() + .then(done) + .catch(e => { + done(e); + }); }); - - after((done) => { + after(done => { done(); // process.exit(); }); - /**************** START : Auth ****************/ - describe('Authentication', function () { + describe('Authentication', function() { this.timeout(10000); - const EMAIL_ID = 'abc@g.com' + const EMAIL_ID = 'abc@g.com'; const VALID_PASSWORD = '1234566778'; - it('Signup with valid email', function (done) { - this.timeout(60000) + it('Signup with valid email', function(done) { + this.timeout(60000); request(app) .post('/auth/signup') - .send({email: EMAIL_ID, password: VALID_PASSWORD}) + .send({ email: EMAIL_ID, password: VALID_PASSWORD }) .expect(200, (err, res) => { if (err) { - expect(res.status).to.equal(400) + expect(res.status).to.equal(400); } else { const token = res.body.token; - expect(token).to.be.a("string") + expect(token).to.be.a('string'); } done(); }); }); - it('Signup with invalid email', (done) => { + it('Signup with invalid email', done => { request(app) .post('/auth/signup') - .send({email: 'test', password: VALID_PASSWORD}) + .send({ email: 'test', password: VALID_PASSWORD }) .expect(400, done); }); - it('Signin with valid credentials', function (done) { + it('Signin with valid credentials', function(done) { request(app) .post('/auth/signin') - .send({email: EMAIL_ID, password: VALID_PASSWORD}) - .expect(200, async function (err, res) { + .send({ email: EMAIL_ID, password: VALID_PASSWORD }) + .expect(200, async function(err, res) { if (err) { return done(err); } @@ -142,11 +144,11 @@ describe('{Auth, CRUD, HasMany, Belongs} Tests', () => { }); }); - it('me', function (done) { + it('me', function(done) { request(app) .get('/user/me') .set('xc-auth', token) - .expect(200, function (err, res) { + .expect(200, function(err, res) { if (err) { return done(err); } @@ -156,68 +158,62 @@ describe('{Auth, CRUD, HasMany, Belongs} Tests', () => { }); }); - - it('Change password', function (done) { + it('Change password', function(done) { request(app) .post('/user/password/change') .set('xc-auth', token) - .send({currentPassword: 'password', newPassword: 'password'}) + .send({ currentPassword: 'password', newPassword: 'password' }) .expect(400, done); }); - - it('Change password - after logout', function (done) { + it('Change password - after logout', function(done) { // todo: request(app) .post('/user/password/change') - .send({currentPassword: 'password', newPassword: 'password'}) - .expect(500, function (_err, _res) { - done() + .send({ currentPassword: 'password', newPassword: 'password' }) + .expect(500, function(_err, _res) { + done(); }); }); - - it('Signin with invalid credentials', function (done) { + it('Signin with invalid credentials', function(done) { request(app) .post('/auth/signin') - .send({email: 'abc@abc.com', password: VALID_PASSWORD}) + .send({ email: 'abc@abc.com', password: VALID_PASSWORD }) .expect(400, done); }); - it('Signin with invalid password', function (done) { + it('Signin with invalid password', function(done) { request(app) .post('/auth/signin') - .send({email: EMAIL_ID, password: 'wrongPassword'}) + .send({ email: EMAIL_ID, password: 'wrongPassword' }) .expect(400, done); }); - - it('Forgot password with a non-existing email id', function (done) { + it('Forgot password with a non-existing email id', function(done) { request(app) .post('/auth/password/forgot') - .send({email: 'abc@abc.com'}) + .send({ email: 'abc@abc.com' }) .expect(400, done); }); - it('Forgot password with an existing email id', function (done) { - this.timeout(10000) + it('Forgot password with an existing email id', function(done) { + this.timeout(10000); request(app) .post('/auth/password/forgot') - .send({email: EMAIL_ID}) + .send({ email: EMAIL_ID }) .expect(200, done); }); - it('Email validate with an invalid token', function (done) { + it('Email validate with an invalid token', function(done) { request(app) .post('/auth/email/validate/someRandomValue') - .send({email: EMAIL_ID}) + .send({ email: EMAIL_ID }) .expect(400, done); }); - it('Email validate with a valid token', function (done) { - - console.log('eeee') - + it('Email validate with a valid token', function(done) { + console.log('eeee'); // todo : done(); @@ -228,18 +224,17 @@ describe('{Auth, CRUD, HasMany, Belongs} Tests', () => { // .expect(500, done); }); - - it('Forgot password validate with an invalid token', function (done) { + it('Forgot password validate with an invalid token', function(done) { request(app) .post('/auth/token/validate/someRandomValue') - .send({email: EMAIL_ID}) + .send({ email: EMAIL_ID }) .expect(400, done); }); - it('Forgot password validate with a valid token', function (done) { + it('Forgot password validate with a valid token', function(done) { // todo - done() + done(); // request(app) // .post('/auth/token/validate/someRandomValue') @@ -247,38 +242,34 @@ describe('{Auth, CRUD, HasMany, Belongs} Tests', () => { // .expect(500, done); }); - - it('Reset Password with an invalid token', function (done) { + it('Reset Password with an invalid token', function(done) { request(app) .post('/auth/password/reset/someRandomValue') - .send({password: 'anewpassword'}) + .send({ password: 'anewpassword' }) .expect(400, done); }); - it('Reset Password with an valid token', function (done) { + it('Reset Password with an valid token', function(done) { //todo - done() + done(); // request(app) // .post('/auth/password/reset/someRandomValue') // .send({password: 'anewpassword'}) // .expect(500, done); }); - - }); - describe('Project', function () { - const EMAIL_ID = 'abc@g.com' + describe('Project', function() { + const EMAIL_ID = 'abc@g.com'; const VALID_PASSWORD = '1234566778'; - - before(function (done) { - this.timeout(120000) + before(function(done) { + this.timeout(120000); request(app) .post('/auth/signin') - .send({email: EMAIL_ID, password: VALID_PASSWORD}) - .expect(200, async function (_err, res) { + .send({ email: EMAIL_ID, password: VALID_PASSWORD }) + .expect(200, async function(_err, res) { token = res.body.token; request(app) .post('/dashboard') @@ -286,210 +277,237 @@ describe('{Auth, CRUD, HasMany, Belongs} Tests', () => { .send(projectCreateReqBody) .expect(200, (err, res) => { if (err) { - return done(err) + return done(err); } projectId = res.body.id; done(); - }) + }); }); - }) - + }); /**************** START : CRUD ****************/ - describe('CRUD', function () { - - + describe('CRUD', function() { let COUNTRY_ID_RET; const COUNTRY_ID = 9999; const COUNTRY_NAME = 'IN'; this.timeout(5000); - it('list + limit : GET - /api/v1/country?limit=6', function (done) { + it('list + limit : GET - /api/v1/country?limit=6', function(done) { console.log(`/nc/${projectId}/api/v1/country?limit=6`); request(app) .get(`/nc/${projectId}/api/v1/country?limit=6`) .set('xc-auth', token) .expect(200, (err, res) => { - if (err) done(err) - expect(res.body.length).to.be.lessThan(7) + if (err) done(err); + expect(res.body.length).to.be.lessThan(7); done(); }); }); - it('list + where : GET - /api/v1/country?where=(country,like,b%)', function (done) { + it('list + where : GET - /api/v1/country?where=(country,like,b%)', function(done) { request(app) .get(`/nc/${projectId}/api/v1/country?where=(country,like,b%)`) .set('xc-auth', token) .expect(200, (err, res) => { - if (err) done(err) + if (err) done(err); expect(res.body).to.be.a('array'); - if (res.body.length) expect(res.body[0].country.toLowerCase()).to.be.a('string').and.satisfy(msg => { - return msg.startsWith('b'); - }, 'Should start with "b"') + if (res.body.length) + expect(res.body[0].country.toLowerCase()) + .to.be.a('string') + .and.satisfy(msg => { + return msg.startsWith('b'); + }, 'Should start with "b"'); done(); }); }); - it('list + sort : GET - /api/v1/country?sort=-country_id', function (done) { + it('list + sort : GET - /api/v1/country?sort=-country_id', function(done) { request(app) .get(`/nc/${projectId}/api/v1/country?sort=-country_id`) .set('xc-auth', token) .expect(200, (err, res) => { - if (err) done(err) + if (err) done(err); expect(res.body).to.be.a('array'); expect(res.body).satisfy(array => { let i = array.length; while (--i) { if (array[i].country_id > array[i - 1].country_id) return false; } - return true - }, 'Should be in descending order') + return true; + }, 'Should be in descending order'); done(); }); }); - it('list + fields : GET - /api/v1/country?fields=country,country_id', function (done) { + it('list + fields : GET - /api/v1/country?fields=country,country_id', function(done) { request(app) .get(`/nc/${projectId}/api/v1/country?fields=country,country_id`) .set('xc-auth', token) .expect(200, (err, res) => { - if (err) done(err) + if (err) done(err); expect(res.body).to.be.a('array'); expect(Object.keys(res.body[0]).length).to.be.equal(3); - expect(res.body[0]).to.have.all.keys('country_id', 'country','cityList'); + expect(res.body[0]).to.have.all.keys( + 'country_id', + 'country', + 'cityList' + ); done(); }); }); - it('list + offset : GET - /api/v1/country?offset=0', function (done) { - + it('list + offset : GET - /api/v1/country?offset=0', function(done) { request(app) .get(`/nc/${projectId}/api/v1/country?offset=0&limit=6`) .set('xc-auth', token) .expect(200, (err, res1) => { - if (err) done(err) + if (err) done(err); request(app) .get(`/nc/${projectId}/api/v1/country?offset=1&limit=5`) .set('xc-auth', token) .expect(200, (err, res2) => { - if (err) done(err) - expect(res2.body).satisfy(arr => arr.every(({country, country_id}, i) => - country === res1.body[i + 1].country && country_id === res1.body[i + 1].country_id - ), 'Both data should need to be equal where offset vary with 1') + if (err) done(err); + expect(res2.body).satisfy( + arr => + arr.every( + ({ country, country_id }, i) => + country === res1.body[i + 1].country && + country_id === res1.body[i + 1].country_id + ), + 'Both data should need to be equal where offset vary with 1' + ); done(); }); }); }); - describe('CRUD', function () { - it('create - POST - /api/v1/country', function (done) { + describe('CRUD', function() { + it('create - POST - /api/v1/country', function(done) { request(app) .delete(`/nc/${projectId}/api/v1/country/` + COUNTRY_ID) .set('xc-auth', token) - .set("xc-auth", token) + .set('xc-auth', token) .expect(200, (_err, _res) => { request(app) .post(`/nc/${projectId}/api/v1/country`) .set('xc-auth', token) - .set("xc-auth", token) - .send({country: COUNTRY_NAME, ...(dbConfig.client === 'mssql' ? {} : {country_id: COUNTRY_ID})}) + .set('xc-auth', token) + .send({ + country: COUNTRY_NAME, + ...(dbConfig.client === 'mssql' + ? {} + : { country_id: COUNTRY_ID }) + }) .expect(200, (err, res) => { - if (err) done(err) + if (err) done(err); COUNTRY_ID_RET = res.body.country_id; expect(res.body).to.be.a('object'); expect(res.body.country).to.be.equal(COUNTRY_NAME); done(); - }) + }); }); }); - - it('read - GET - /api/v1/country/:id', function (done) { + it('read - GET - /api/v1/country/:id', function(done) { request(app) .get(`/nc/${projectId}/api/v1/country/1`) .set('xc-auth', token) .expect(200, (err, res) => { - if (err) done(err) + if (err) done(err); // expect(res.body).to.be.a('array'); expect(res.body).to.be.a('object'); expect(res.body.country).to.be.equal('Afghanistan'); done(); - }) + }); }); - it('update - PUT - /api/v1/country/:id', function (done) { + it('update - PUT - /api/v1/country/:id', function(done) { request(app) - .put(`/nc/${projectId}/api/v1/country/` + (dbConfig.client === 'mssql' ? COUNTRY_ID_RET :COUNTRY_ID)) + .put( + `/nc/${projectId}/api/v1/country/` + + (dbConfig.client === 'mssql' ? COUNTRY_ID_RET : COUNTRY_ID) + ) .set('xc-auth', token) - .set("xc-auth", token) - .send({country: COUNTRY_NAME + 'a'}) + .set('xc-auth', token) + .send({ country: COUNTRY_NAME + 'a' }) .expect(200, (err, res) => { - if (err) done(err) + if (err) done(err); expect(res.body).to.be.a('object'); request(app) - .get(`/nc/${projectId}/api/v1/country/` + (dbConfig.client === 'mssql' ? COUNTRY_ID_RET :COUNTRY_ID)) + .get( + `/nc/${projectId}/api/v1/country/` + + (dbConfig.client === 'mssql' ? COUNTRY_ID_RET : COUNTRY_ID) + ) .set('xc-auth', token) .expect(200, (err, res) => { - if (err) done(err) + if (err) done(err); expect(res.body).to.be.a('object'); expect(res.body.country).to.be.equal(COUNTRY_NAME + 'a'); done(); - }) - }) + }); + }); }); - it('exists - GET - /api/v1/country/:id/exists', function (done) { + it('exists - GET - /api/v1/country/:id/exists', function(done) { request(app) .get(`/nc/${projectId}/api/v1/country/1/exists`) .set('xc-auth', token) .expect(200, (err, res) => { - if (err) done(err) + if (err) done(err); expect(res.body).to.be.true; done(); - }) + }); }); - it('findOne - GET - /api/v1/country/findOne', function (done) { + it('findOne - GET - /api/v1/country/findOne', function(done) { request(app) - .get(`/nc/${projectId}/api/v1/country/findOne?where=(country,eq,${COUNTRY_NAME + 'a'})`) + .get( + `/nc/${projectId}/api/v1/country/findOne?where=(country,eq,${COUNTRY_NAME + + 'a'})` + ) .set('xc-auth', token) .expect(200, (err, res) => { - if (err) done(err) + if (err) done(err); expect(res.body).to.be.a('object'); expect(res.body.country).to.be.equal(COUNTRY_NAME + 'a'); done(); }); - }) + }); - it('delete - DELETE - /api/v1/country/:id', function (done) { + it('delete - DELETE - /api/v1/country/:id', function(done) { request(app) - .delete(`/nc/${projectId}/api/v1/country/` + (dbConfig.client === 'mssql' ? COUNTRY_ID_RET :COUNTRY_ID)) + .delete( + `/nc/${projectId}/api/v1/country/` + + (dbConfig.client === 'mssql' ? COUNTRY_ID_RET : COUNTRY_ID) + ) + .set('xc-auth', token) .set('xc-auth', token) - .set("xc-auth", token) .expect(200, (err, res) => { - if (err) done(err) + if (err) done(err); expect(res.body).to.be.equal(1); request(app) - .get(`/nc/${projectId}/api/v1/country/` + (dbConfig.client === 'mssql' ? COUNTRY_ID_RET :COUNTRY_ID)) + .get( + `/nc/${projectId}/api/v1/country/` + + (dbConfig.client === 'mssql' ? COUNTRY_ID_RET : COUNTRY_ID) + ) .set('xc-auth', token) .expect(200, (err, res) => { - if (err) done(err) + if (err) done(err); expect(res.body).to.be.a('object'); expect(Object.keys(res.body)).to.have.length(0); done(); - }) - }) + }); + }); }); - - }) + }); if (dbConfig.client !== 'mssql') { - it('groupBy - GET - /api/v1/country/groupby/:cn', function (done) { + it('groupBy - GET - /api/v1/country/groupby/:cn', function(done) { request(app) .get(`/nc/${projectId}/api/v1/country/groupby/country?limit=5`) .set('xc-auth', token) .expect(200, (err, res) => { - if (err) done(err) + if (err) done(err); expect(res.body).to.be.a('array'); if (res.body.length) { expect(res.body.length).to.be.most(5); @@ -499,15 +517,16 @@ describe('{Auth, CRUD, HasMany, Belongs} Tests', () => { } done(); }); - }) - + }); - it('groupBy multiple - GET - /api/v1/country/groupby/:cn', function (done) { + it('groupBy multiple - GET - /api/v1/country/groupby/:cn', function(done) { request(app) - .get(`/nc/${projectId}/api/v1/country/groupby/country?fields=country_id&limit=5`) + .get( + `/nc/${projectId}/api/v1/country/groupby/country?fields=country_id&limit=5` + ) .set('xc-auth', token) .expect(200, (err, res) => { - if (err) done(err) + if (err) done(err); expect(res.body).to.be.a('array'); expect(res.body.length).to.be.most(5); if (res.body.length) { @@ -518,31 +537,38 @@ describe('{Auth, CRUD, HasMany, Belongs} Tests', () => { } done(); }); - }) + }); // todo: change distribute => distribution - it('distribution - GET - /api/v1/country/distribute', function (done) { + it('distribution - GET - /api/v1/country/distribute', function(done) { request(app) - .get(`/nc/${projectId}/api/v1/country/distribute?column_name=country_id&steps=1,34,50`) + .get( + `/nc/${projectId}/api/v1/country/distribute?column_name=country_id&steps=1,34,50` + ) .set('xc-auth', token) .expect(200, (err, res) => { - if (err) done(err) + if (err) done(err); expect(res.body).to.be.a('array'); expect(+res.body[0].count).to.be.a('number'); - expect(+res.body[0].count).satisfies(num => num === parseInt(num) && num >= 0, 'should be a positive integer'); + expect(+res.body[0].count).satisfies( + num => num === parseInt(num) && num >= 0, + 'should be a positive integer' + ); expect(res.body[0].range).to.be.a('string'); - expect(res.body[0].range).to.be.match(/^\d+-\d+$/, 'should match {num start}-{num end} format') + expect(res.body[0].range).to.be.match( + /^\d+-\d+$/, + 'should match {num start}-{num end} format' + ); done(); }); - }) - + }); - it('distinct - GET - /api/v1/country/distinct', function (done) { + it('distinct - GET - /api/v1/country/distinct', function(done) { request(app) .get(`/nc/${projectId}/api/v1/country/distinct?cn=country&limit=5`) .set('xc-auth', token) .expect(200, (err, res) => { - if (err) done(err) + if (err) done(err); expect(res.body).to.be.a('array'); if (res.body.length) { expect(res.body[0].country).to.be.a('string'); @@ -551,14 +577,16 @@ describe('{Auth, CRUD, HasMany, Belongs} Tests', () => { expect(res.body.length).to.be.most(5); done(); }); - }) + }); - it('distinct multiple - GET - /api/v1/country/distinct/:cn', function (done) { + it('distinct multiple - GET - /api/v1/country/distinct/:cn', function(done) { request(app) - .get(`/nc/${projectId}/api/v1/country/distinct?cn=country&fields=country_id&limit=5`) + .get( + `/nc/${projectId}/api/v1/country/distinct?cn=country&fields=country_id&limit=5` + ) .set('xc-auth', token) .expect(200, (err, res) => { - if (err) done(err) + if (err) done(err); expect(res.body).to.be.a('array'); if (res.body.length) { expect(res.body[0].country).to.be.a('string'); @@ -567,229 +595,249 @@ describe('{Auth, CRUD, HasMany, Belongs} Tests', () => { expect(res.body.length).to.be.most(5); done(); }); - }) - + }); - it('aggregate - GET - /api/v1/country/aggregate', function (done) { + it('aggregate - GET - /api/v1/country/aggregate', function(done) { request(app) - .get(`/nc/${projectId}/api/v1/country/aggregate?column_name=country_id&func=sum,avg,min,max,count`) - .set('xc-auth', token).expect(200, (err, res) => { - if (err) done(err) - expect(res.body).to.be.a('array'); - if (res.body.length) { - expect(+res.body[0].min).to.be.a('number'); - expect(+res.body[0].max).to.be.a('number'); - expect(+res.body[0].avg).to.be.satisfy(num => !isNaN(parseInt(num)), 'count should be an number'); - expect(+res.body[0].sum).to.be.satisfy(num => !isNaN(parseInt(num)), 'count should be an number'); - expect(+res.body[0].count).to.be.a('number').and.satisfy(num => num === parseInt(num), 'count should be an integer'); - // expect(Object.keys(res.body[0]).length).to.be.equal(7); - } - expect(res.body.length).to.be.most(20); - done(); - }); - }) - + .get( + `/nc/${projectId}/api/v1/country/aggregate?column_name=country_id&func=sum,avg,min,max,count` + ) + .set('xc-auth', token) + .expect(200, (err, res) => { + if (err) done(err); + expect(res.body).to.be.a('array'); + if (res.body.length) { + expect(+res.body[0].min).to.be.a('number'); + expect(+res.body[0].max).to.be.a('number'); + expect(+res.body[0].avg).to.be.satisfy( + num => !isNaN(parseInt(num)), + 'count should be an number' + ); + expect(+res.body[0].sum).to.be.satisfy( + num => !isNaN(parseInt(num)), + 'count should be an number' + ); + expect(+res.body[0].count) + .to.be.a('number') + .and.satisfy( + num => num === parseInt(num), + 'count should be an integer' + ); + // expect(Object.keys(res.body[0]).length).to.be.equal(7); + } + expect(res.body.length).to.be.most(20); + done(); + }); + }); - it('count - GET - /api/v1/country/count', function (done) { + it('count - GET - /api/v1/country/count', function(done) { request(app) .get(`/nc/${projectId}/api/v1/country/count`) - .set('xc-auth', token).expect(200, (err, res) => { - if (err) done(err) - expect(res.body).to.be.a('object'); - expect(+res.body.count).to.be.a('number').and.satisfy(num => num === parseInt(num), 'count should be an integer'); - done(); - }); - }) + .set('xc-auth', token) + .expect(200, (err, res) => { + if (err) done(err); + expect(res.body).to.be.a('object'); + expect(+res.body.count) + .to.be.a('number') + .and.satisfy( + num => num === parseInt(num), + 'count should be an integer' + ); + done(); + }); + }); if (dbConfig.client !== 'sqlite3') { - it('bulk insert - POST - /api/v1/country/bulk', function (done) { + it('bulk insert - POST - /api/v1/country/bulk', function(done) { request(app) .post(`/nc/${projectId}/api/v1/country/bulk`) - .set("xc-auth", token) + .set('xc-auth', token) .send([ - {country: 'a'}, - {country: 'b'}, - {country: 'c'}, - {country: 'd'}, - {country: 'e'}, + { country: 'a' }, + { country: 'b' }, + { country: 'c' }, + { country: 'd' }, + { country: 'e' } ]) .expect(200, (err, res) => { - if (err) done(err) + if (err) done(err); expect(res.body).to.be.a('array'); expect(res.body[0]).to.be.a('number'); request(app) .get(`/nc/${projectId}/api/v1/country/${res.body.pop()}`) - .set('xc-auth', token).expect(200, (err, res) => { - if (err) done(err) - // expect(res.body).to.be.a('array'); - expect(res.body).to.be.a('object'); - // in mysql it will be a and in pg : e - expect(['a', 'e'].indexOf(res.body.country)).to.be.greaterThan(-1); - done() - }) + .set('xc-auth', token) + .expect(200, (err, res) => { + if (err) done(err); + // expect(res.body).to.be.a('array'); + expect(res.body).to.be.a('object'); + // in mysql it will be a and in pg : e + expect( + ['a', 'e'].indexOf(res.body.country) + ).to.be.greaterThan(-1); + done(); + }); }); - }) - + }); - it('bulk update - PUT - /api/v1/country/bulk', function (done) { + it('bulk update - PUT - /api/v1/country/bulk', function(done) { // get last inserted 5 entry by sorting db data in reverse order based on id request(app) .get(`/nc/${projectId}/api/v1/country?sort=-country_id&limit=5`) - .set('xc-auth', token).expect(200, (err, res) => { - if (err) done(err); - - - expect(res.body).to.be.a('array'); - expect(res.body[0]).to.be.a('object'); - expect(res.body[0].country).to.be.a('string'); + .set('xc-auth', token) + .expect(200, (err, res) => { + if (err) done(err); + expect(res.body).to.be.a('array'); + expect(res.body[0]).to.be.a('object'); + expect(res.body[0].country).to.be.a('string'); - request(app) - .put(`/nc/${projectId}/api/v1/country/bulk`) - .set("xc-auth", token) - .send( - res.body.map(({country, country_id}) => ({ - country_id, - country: country + 1 - })) - ) - .expect(200, (err, res) => { - if (err) done(err) - expect(res.body).to.be.a('array'); - expect(res.body[0]).to.be.a('number'); - expect(res.body[0]).to.be.equal(1); - expect(res.body.length).to.be.equal(5); - done() - }); - }) - }) - it('bulk delete - DELETE - /api/v1/country/bulk', function (done) { + request(app) + .put(`/nc/${projectId}/api/v1/country/bulk`) + .set('xc-auth', token) + .send( + res.body.map(({ country, country_id }) => ({ + country_id, + country: country + 1 + })) + ) + .expect(200, (err, res) => { + if (err) done(err); + expect(res.body).to.be.a('array'); + expect(res.body[0]).to.be.a('number'); + expect(res.body[0]).to.be.equal(1); + expect(res.body.length).to.be.equal(5); + done(); + }); + }); + }); + it('bulk delete - DELETE - /api/v1/country/bulk', function(done) { // get last inserted 5 entry by sorting db data in reverse order based on id request(app) .get(`/nc/${projectId}/api/v1/country?sort=-country_id&limit=5`) - .set('xc-auth', token).expect(200, (err, res) => { - if (err) done(err); - expect(res.body).to.be.a('array'); - expect(res.body[0]).to.be.a('object'); - expect(res.body[0].country).to.be.a('string'); + .set('xc-auth', token) + .expect(200, (err, res) => { + if (err) done(err); + expect(res.body).to.be.a('array'); + expect(res.body[0]).to.be.a('object'); + expect(res.body[0].country).to.be.a('string'); - request(app) - .delete(`/nc/${projectId}/api/v1/country/bulk`) - .set("xc-auth", token) - .send( - res.body.map(({country_id}) => ({country_id})) - ) - .expect(200, (err, res) => { - if (err) done(err) - expect(res.body).to.be.a('array'); - expect(res.body[0]).to.be.a('number'); - expect(res.body[0]).to.be.equal(1); - expect(res.body.length).to.be.equal(5); - done() - }); - }) - }) + request(app) + .delete(`/nc/${projectId}/api/v1/country/bulk`) + .set('xc-auth', token) + .send(res.body.map(({ country_id }) => ({ country_id }))) + .expect(200, (err, res) => { + if (err) done(err); + expect(res.body).to.be.a('array'); + expect(res.body[0]).to.be.a('number'); + expect(res.body[0]).to.be.equal(1); + expect(res.body.length).to.be.equal(5); + done(); + }); + }); + }); } } - }); /**************** END : CRUD ****************/ - if (dbConfig.client !== 'mssql' && dbConfig.client !== 'sqlite3') { /**************** START : hasMany ****************/ - describe('Country HasMany City Api', function () { - const CITY_NAME = 'testCity', CITY_ID = '9999'; + describe('Country HasMany City Api', function() { + const CITY_NAME = 'testCity', + CITY_ID = '9999'; - it('has city - GET - /api/v1/country/has/city(:childs)?', function (done) { + it('has city - GET - /api/v1/country/has/city(:childs)?', function(done) { // get last inserted 5 entry by sorting db data in reverse order based on id request(app) .get(`/nc/${projectId}/api/v1/country/has/city`) - .set('xc-auth', token).expect(200, (err, res) => { - if (err) done(err); - expect(res.body).to.be.a('array'); - expect(res.body[0]).to.be.a('object'); - expect(res.body[0].country).to.be.a('string'); - expect(res.body[0].cityList).to.be.a('array'); - expect(res.body[0].cityList[0]).to.be.a('object'); - expect(res.body[0].cityList[0].city).to.be.a('string'); - done(); - }) - }) + .set('xc-auth', token) + .expect(200, (err, res) => { + if (err) done(err); + expect(res.body).to.be.a('array'); + expect(res.body[0]).to.be.a('object'); + expect(res.body[0].country).to.be.a('string'); + expect(res.body[0].cityList).to.be.a('array'); + expect(res.body[0].cityList[0]).to.be.a('object'); + expect(res.body[0].cityList[0].city).to.be.a('string'); + done(); + }); + }); - it('cities under a single parent - GET - /api/v1/country/:parentId/city', function (done) { + it('cities under a single parent - GET - /api/v1/country/:parentId/city', function(done) { // get last inserted 5 entry by sorting db data in reverse order based on id request(app) .get(`/nc/${projectId}/api/v1/country/1/city?limit=5`) - .set('xc-auth', token).expect(200, (err, res) => { - if (err) done(err); - expect(res.body).to.be.a('array'); - expect(res.body[0]).to.be.a('object'); - expect(res.body[0].city).to.be.a('string'); - expect(res.body.length).to.be.most(5); - done(); - }) - }) - + .set('xc-auth', token) + .expect(200, (err, res) => { + if (err) done(err); + expect(res.body).to.be.a('array'); + expect(res.body[0]).to.be.a('object'); + expect(res.body[0].city).to.be.a('string'); + expect(res.body.length).to.be.most(5); + done(); + }); + }); - it('create - POST - /api/v1/country/:parentId/city/:id', function (done) { + it('create - POST - /api/v1/country/:parentId/city/:id', function(done) { request(app) .delete(`/nc/${projectId}/api/v1/country/1/city/${CITY_ID}`) - .set('xc-auth', token).set("xc-auth", token) + .set('xc-auth', token) + .set('xc-auth', token) .expect(200, (_err, _res) => { request(app) .post(`/nc/${projectId}/api/v1/country/1/city`) - .set("xc-auth", token) - .send({city: CITY_NAME, city_id: CITY_ID}) + .set('xc-auth', token) + .send({ city: CITY_NAME, city_id: CITY_ID }) .expect(200, (err, res) => { if (err) done(err); - expect(res.body).to.be.a('object') + expect(res.body).to.be.a('object'); expect(res.body.city).to.be.equal(CITY_NAME); - expect(res.body.country_id + "").to.be.equal("1"); + expect(res.body.country_id + '').to.be.equal('1'); done(); }); }); }); - it('get city by id - GET - /api/v1/country/:parentId/city/:id', function (done) { + it('get city by id - GET - /api/v1/country/:parentId/city/:id', function(done) { request(app) .get(`/nc/${projectId}/api/v1/country/1/city/${CITY_ID}`) - .set('xc-auth', token).expect(200, (err, res) => { - if (err) done(err); - expect(res.body).to.be.a('array') - expect(res.body[0].city).to.be.equal(CITY_NAME); - expect(res.body[0].country_id).to.be.equal(1); - done(); - }); + .set('xc-auth', token) + .expect(200, (err, res) => { + if (err) done(err); + expect(res.body).to.be.a('array'); + expect(res.body[0].city).to.be.equal(CITY_NAME); + expect(res.body[0].country_id).to.be.equal(1); + done(); + }); }); - it('get count - GET - /api/v1/country/:parentId/city/count', function (done) { + it('get count - GET - /api/v1/country/:parentId/city/count', function(done) { request(app) .get(`/nc/${projectId}/api/v1/country/1/city/count`) - .set('xc-auth', token).expect(200, (err, res) => { - if (err) done(err); - expect(res.body).to.be.a('object') - expect(+res.body.count).to.be.a('number'); - done(); - }); + .set('xc-auth', token) + .expect(200, (err, res) => { + if (err) done(err); + expect(res.body).to.be.a('object'); + expect(+res.body.count).to.be.a('number'); + done(); + }); }); - - it('update - PUT - /api/v1/country/:parentId/city/:id', function (done) { + it('update - PUT - /api/v1/country/:parentId/city/:id', function(done) { request(app) .put(`/nc/${projectId}/api/v1/country/1/city/${CITY_ID}`) - .set("xc-auth", token) - .send({city: CITY_NAME + 'a'}) + .set('xc-auth', token) + .send({ city: CITY_NAME + 'a' }) .expect(200, (err, res) => { if (err) done(err); - expect(res.body).to.be.equal(1) + expect(res.body).to.be.equal(1); request(app) .get(`/nc/${projectId}/api/v1/country/1/city/${CITY_ID}`) - .set("xc-auth", token) + .set('xc-auth', token) .expect(200, (err, res) => { if (err) done(err); - expect(res.body).to.be.a('array') + expect(res.body).to.be.a('array'); expect(res.body[0].city).to.be.equal(CITY_NAME + 'a'); expect(res.body[0].country_id).to.be.equal(1); done(); @@ -797,49 +845,50 @@ describe('{Auth, CRUD, HasMany, Belongs} Tests', () => { }); }); - - it('findOne city - GET - /api/v1/country/:parentId/city/findOne', function (done) { + it('findOne city - GET - /api/v1/country/:parentId/city/findOne', function(done) { request(app) - .get(`/nc/${projectId}/api/v1/country/1/city/findOne?where=(city,eq,${CITY_NAME + 'a'})`) - .set('xc-auth', token).expect(200, (err, res) => { - if (err) done(err); - expect(res.body).to.be.a('object') - expect(res.body.city).to.be.equal(CITY_NAME + 'a'); - expect(res.body.country_id + "").to.be.equal("1"); - done(); - }); + .get( + `/nc/${projectId}/api/v1/country/1/city/findOne?where=(city,eq,${CITY_NAME + + 'a'})` + ) + .set('xc-auth', token) + .expect(200, (err, res) => { + if (err) done(err); + expect(res.body).to.be.a('object'); + expect(res.body.city).to.be.equal(CITY_NAME + 'a'); + expect(res.body.country_id + '').to.be.equal('1'); + done(); + }); }); - - it('exists city - GET - /api/v1/country/1/city/${CITY_ID}/exists', function (done) { + it('exists city - GET - /api/v1/country/1/city/${CITY_ID}/exists', function(done) { request(app) .get(`/nc/${projectId}/api/v1/country/1/city/${CITY_ID}/exists`) - .set('xc-auth', token).expect(200, (err, res) => { - if (err) done(err); - expect(res.body).to.be.true - done(); - }); + .set('xc-auth', token) + .expect(200, (err, res) => { + if (err) done(err); + expect(res.body).to.be.true; + done(); + }); }); - it('delete - DELETE - /api/v1/country/:parentId/city', function (done) { - this.timeout(10000) + it('delete - DELETE - /api/v1/country/:parentId/city', function(done) { + this.timeout(10000); request(app) .delete(`/nc/${projectId}/api/v1/country/1/city/${CITY_ID}`) - .set("xc-auth", token) + .set('xc-auth', token) .expect(200, (err, res) => { if (err) done(err); - expect(res.body).to.be.equal(1) + expect(res.body).to.be.equal(1); done(); }); }); - - }) + }); /**************** END : hasMany ****************/ /**************** START : belongsTo ****************/ - describe('City BelngsTo Country Api', function () { - - it('city belongs to country - GET - /api/v1/city/belongs/country', function (done) { + describe('City BelngsTo Country Api', function() { + it('city belongs to country - GET - /api/v1/city/belongs/country', function(done) { // get last inserted 5 entry by sorting db data in reverse order based on id request(app) .get(`/nc/${projectId}/api/v1/city/belongs/country?limit=10`) @@ -850,16 +899,16 @@ describe('{Auth, CRUD, HasMany, Belongs} Tests', () => { expect(res.body[0]).to.be.a('object'); expect(res.body[0].city).to.be.a('string'); expect(res.body[0].countryRead).to.be.a('object'); - expect(res.body.length).to.be.most(10) + expect(res.body.length).to.be.most(10); done(); - }) - }) + }); + }); }); /**************** END : belongsTo ****************/ } }); - -});/** +}); +/** * @copyright Copyright (c) 2021, Xgene Cloud Ltd * * @author Naveen MR diff --git a/packages/nocodb/src/example/docker.ts b/packages/nocodb/src/example/docker.ts index db2d3d044c..ac30d8bcdd 100644 --- a/packages/nocodb/src/example/docker.ts +++ b/packages/nocodb/src/example/docker.ts @@ -1,13 +1,15 @@ import cors from 'cors'; import express from 'express'; -import Noco from "../lib/noco/Noco"; +import Noco from '../lib/noco/Noco'; process.env.NC_VERSION = '0009044'; const server = express(); -server.use(cors({ - exposedHeaders: 'xc-db-response' -})); +server.use( + cors({ + exposedHeaders: 'xc-db-response' + }) +); server.set('view engine', 'ejs'); @@ -17,16 +19,14 @@ server.set('view engine', 'ejs'); // process.env[`NC_TRY`] = 'true'; // process.env[`NC_DASHBOARD_URL`] = '/test'; - process.env[`DEBUG`] = 'xc*'; (async () => { server.use(await Noco.init({})); server.listen(process.env.PORT || 8080, () => { console.log(`App started successfully.\nVisit -> ${Noco.dashboardUrl}`); - }) -})().catch(e => console.log(e)) - + }); +})().catch(e => console.log(e)); /** * @copyright Copyright (c) 2021, Xgene Cloud Ltd diff --git a/packages/nocodb/src/example/try.ts b/packages/nocodb/src/example/try.ts index eefbbbc94d..a4c2d5324a 100644 --- a/packages/nocodb/src/example/try.ts +++ b/packages/nocodb/src/example/try.ts @@ -1,38 +1,42 @@ import cors from 'cors'; import express from 'express'; -import {NcConfigFactory, Noco} from "../lib"; - - -process.env.DATABASE_URL = 'mysql://root:password@localhost:3306/sakila' +import { NcConfigFactory, Noco } from '../lib'; +process.env.DATABASE_URL = 'mysql://root:password@localhost:3306/sakila'; const url = NcConfigFactory.extractXcUrlFromJdbc(process.env.DATABASE_URL); process.env.NC_DB = url; - (async () => { - const server = express(); - server.use(cors()); - server.set('view engine', 'ejs'); - const app = new Noco(); - server.use(await app.init({ + const server = express(); + server.use(cors()); + server.set('view engine', 'ejs'); + const app = new Noco(); + server.use( + await app.init({ async afterMetaMigrationInit(): Promise { if (!(await app.ncMeta.projectList())?.length) { const config = NcConfigFactory.makeProjectConfigFromUrl(url); - const project = await app.ncMeta.projectCreate(config.title, config, ''); + const project = await app.ncMeta.projectCreate( + config.title, + config, + '' + ); await app.ncMeta.projectStatusUpdate(config.title, 'started'); await app.ncMeta.projectAddUser(project.id, 1, 'owner,creator'); } } - })); - server.listen(process.env.PORT || 8080, () => { - console.log(`App started successfully.\nVisit -> http://localhost:${process.env.PORT || 8080}/xc`); }) - } -)().catch(e => console.log(e)) - + ); + server.listen(process.env.PORT || 8080, () => { + console.log( + `App started successfully.\nVisit -> http://localhost:${process.env + .PORT || 8080}/xc` + ); + }); +})().catch(e => console.log(e)); /** * @copyright Copyright (c) 2021, Xgene Cloud Ltd diff --git a/packages/nocodb/src/interface/IEmailAdapter.ts b/packages/nocodb/src/interface/IEmailAdapter.ts index b5218c2b44..c49f6b1b47 100644 --- a/packages/nocodb/src/interface/IEmailAdapter.ts +++ b/packages/nocodb/src/interface/IEmailAdapter.ts @@ -1,21 +1,18 @@ export default interface IEmailAdapter { - init(): Promise - mailSend(mail:XcEmail): Promise - test(email): Promise + init(): Promise; + mailSend(mail: XcEmail): Promise; + test(email): Promise; } - interface XcEmail { // from?:string; - to:string; - subject:string; - html?:string; - text?:string; + to: string; + subject: string; + html?: string; + text?: string; } -export { - XcEmail -} +export { XcEmail }; /** * @copyright Copyright (c) 2021, Xgene Cloud Ltd diff --git a/packages/nocodb/src/interface/IStorageAdapter.ts b/packages/nocodb/src/interface/IStorageAdapter.ts index 1ab5b8da36..5aeca6e1ee 100644 --- a/packages/nocodb/src/interface/IStorageAdapter.ts +++ b/packages/nocodb/src/interface/IStorageAdapter.ts @@ -1,12 +1,11 @@ export default interface IStorageAdapter { - init(): Promise - fileCreate(destPath: string, file: XcFile): Promise - fileDelete(filePath: string): Promise - fileRead(filePath: string): Promise - test(): Promise + init(): Promise; + fileCreate(destPath: string, file: XcFile): Promise; + fileDelete(filePath: string): Promise; + fileRead(filePath: string): Promise; + test(): Promise; } - interface XcFile { originalname: string; path: string; @@ -14,9 +13,7 @@ interface XcFile { size: number | string; } -export { - XcFile -} +export { XcFile }; /** * @copyright Copyright (c) 2021, Xgene Cloud Ltd diff --git a/packages/nocodb/src/interface/XcDynamicChanges.ts b/packages/nocodb/src/interface/XcDynamicChanges.ts index ae9c7cef5b..719c36e5e0 100644 --- a/packages/nocodb/src/interface/XcDynamicChanges.ts +++ b/packages/nocodb/src/interface/XcDynamicChanges.ts @@ -1,13 +1,12 @@ export default interface XcDynamicChanges { onTableCreate(tn: string): Promise; - onTableUpdate(changeObj:any):Promise; + onTableUpdate(changeObj: any): Promise; onTableDelete(tn: string): Promise; onTableRename(oldTableName: string, newTableName: string): Promise; onHandlerCodeUpdate(tn: string): Promise; - onMetaUpdate(tn:string):Promise; + onMetaUpdate(tn: string): Promise; } - /** * @copyright Copyright (c) 2021, Xgene Cloud Ltd * diff --git a/packages/nocodb/src/interface/XcMetaMgr.ts b/packages/nocodb/src/interface/XcMetaMgr.ts index 992b2678c0..ac58bf0261 100644 --- a/packages/nocodb/src/interface/XcMetaMgr.ts +++ b/packages/nocodb/src/interface/XcMetaMgr.ts @@ -1,9 +1,5 @@ - // eslint-disable-next-line @typescript-eslint/no-empty-interface -export default interface XcMetaMgr { - - -} +export default interface XcMetaMgr {} /** * @copyright Copyright (c) 2021, Xgene Cloud Ltd * diff --git a/packages/nocodb/src/interface/config.ts b/packages/nocodb/src/interface/config.ts index b5ca65b39f..f0f5a1661f 100644 --- a/packages/nocodb/src/interface/config.ts +++ b/packages/nocodb/src/interface/config.ts @@ -1,6 +1,6 @@ -import {Handler} from "express"; +import { Handler } from 'express'; import * as e from 'express'; -import Knex from "knex"; +import Knex from 'knex'; export interface Route { path: string; @@ -13,7 +13,6 @@ export interface Route { functions?: string[]; } - export enum RouteType { GET = 'get', POST = 'post', @@ -24,30 +23,30 @@ export enum RouteType { OPTIONS = 'options' } -type InflectionTypes = 'pluralize' | - 'singularize' | - 'inflect' | - 'camelize' | - 'underscore' | - 'humanize' | - 'capitalize' | - 'dasherize' | - 'titleize' | - 'demodulize' | - 'tableize' | - 'classify' | - 'foreign_key' | - 'ordinalize' | - 'transform' | 'none' ; +type InflectionTypes = + | 'pluralize' + | 'singularize' + | 'inflect' + | 'camelize' + | 'underscore' + | 'humanize' + | 'capitalize' + | 'dasherize' + | 'titleize' + | 'demodulize' + | 'tableize' + | 'classify' + | 'foreign_key' + | 'ordinalize' + | 'transform' + | 'none'; export interface DbConfig extends Knex.Config { - client: string; connection: Knex.StaticConnectionConfig | Knex.Config | any; meta: { - dbAlias: string; metaTables?: 'db' | 'file'; @@ -73,7 +72,7 @@ export interface DbConfig extends Knex.Config { type: 'rest' | 'graphql' | 'grpc'; prefix: string; swagger?: boolean; - graphiql?: boolean + graphiql?: boolean; graphqlDepthLimit?: number; }; @@ -86,15 +85,14 @@ export interface DbConfig extends Knex.Config { print?: boolean; explain?: boolean; measure?: boolean; - }, + }; reset?: boolean; - dbtype?: "vitess" | string; + dbtype?: 'vitess' | string; pluralize?: boolean; inflection?: { tn?: InflectionTypes; cn?: InflectionTypes; - } - + }; }; } @@ -117,19 +115,19 @@ export interface AuthConfig { secret: string; [key: string]: any; dbAlias?: string; - options?: JwtOptions - }, + options?: JwtOptions; + }; masterKey?: { - secret: string + secret: string; }; middleware?: { url: string; - }, + }; disabled?: boolean; } export interface MiddlewareConfig { - handler?: (...args: any[]) => any + handler?: (...args: any[]) => any; } export interface ACLConfig { @@ -138,31 +136,30 @@ export interface ACLConfig { } export interface MailerConfig { - [key: string]: any + [key: string]: any; } export interface ServerlessConfig { aws?: { - lambda: boolean + lambda: boolean; }; gcp?: { - cloudFunction: boolean + cloudFunction: boolean; }; azure?: { - cloudFunctionApp: boolean + cloudFunctionApp: boolean; }; zeit?: { - now: boolean + now: boolean; }; alibaba?: { - functionCompute: boolean + functionCompute: boolean; }; serverlessFramework?: { - http: boolean + http: boolean; }; } - export interface NcGui { path?: string; disabled?: boolean; @@ -177,12 +174,11 @@ export interface NcConfig { envs: { [key: string]: { - db: DbConfig[], - api?: any, + db: DbConfig[]; + api?: any; publicUrl?: string; - } - } - + }; + }; // dbs: DbConfig[]; @@ -197,20 +193,19 @@ export interface NcConfig { make?: () => NcConfig; serverless?: ServerlessConfig; - toolDir?: string; - env?: 'production' | 'dev' | 'test' | string, - workingEnv?: string, - - seedsFolder?: string | string[], - queriesFolder?: string | string[], - apisFolder?: string | string[], - projectType?: "rest" | "graphql" | "grpc", - type?: "mvc" | "package" | "docker", - language?: "ts" | "js", + env?: 'production' | 'dev' | 'test' | string; + workingEnv?: string; + + seedsFolder?: string | string[]; + queriesFolder?: string | string[]; + apisFolder?: string | string[]; + projectType?: 'rest' | 'graphql' | 'grpc'; + type?: 'mvc' | 'package' | 'docker'; + language?: 'ts' | 'js'; meta?: { - db?: any - }, + db?: any; + }; api?: any; gui?: NcGui; try?: boolean; @@ -219,51 +214,52 @@ export interface NcConfig { prefix?: string; publicUrl?: string; - } export interface Event { title: string; tn: string; - url - headers - operation - event - retry - max - interval - timeout + url; + headers; + operation; + event; + retry; + max; + interval; + timeout; } - export interface Acl { - [role: string]: { - create: boolean | ColumnAcl - [key: string]: boolean | ColumnAcl - } | boolean |any + [role: string]: + | { + create: boolean | ColumnAcl; + [key: string]: boolean | ColumnAcl; + } + | boolean + | any; } export interface ColumnAcl { columns: { - [cn: string]: boolean - }, + [cn: string]: boolean; + }; assign?: { - [cn: string]: any - } + [cn: string]: any; + }; } export interface Acls { - [tn: string]: Acl + [tn: string]: Acl; } export enum ServerlessType { - AWS_LAMBDA = "AWS_LAMBDA", - GCP_FUNCTION = "GCP_FUNCTION", - AZURE_FUNCTION_APP = "AZURE_FUNCTION_APP", - ALIYUN = "ALIYUN", - ZEIT = "ZEIT", - LYRID = "LYRID", - SERVERLESS = "SERVERLESS" + AWS_LAMBDA = 'AWS_LAMBDA', + GCP_FUNCTION = 'GCP_FUNCTION', + AZURE_FUNCTION_APP = 'AZURE_FUNCTION_APP', + ALIYUN = 'ALIYUN', + ZEIT = 'ZEIT', + LYRID = 'LYRID', + SERVERLESS = 'SERVERLESS' } export class Result { @@ -276,12 +272,8 @@ export class Result { this.message = message; this.data = data; } - } - - - enum HTTPType { GET = 'get', POST = 'post', @@ -290,7 +282,6 @@ enum HTTPType { PATCH = 'patch', HEAD = 'head', OPTIONS = 'options' - } export interface XcRoute { diff --git a/packages/nocodb/src/lib/dataMapper/index.ts b/packages/nocodb/src/lib/dataMapper/index.ts index a0472d7eae..e67cd99537 100644 --- a/packages/nocodb/src/lib/dataMapper/index.ts +++ b/packages/nocodb/src/lib/dataMapper/index.ts @@ -1,9 +1,9 @@ -export {DbFactory} from './lib/DbFactory'; -export {BaseModelSql} from './lib/sql/BaseModelSql'; +export { DbFactory } from './lib/DbFactory'; +export { BaseModelSql } from './lib/sql/BaseModelSql'; -import XKnex, {Knex} from './lib/sql/CustomKnex'; +import XKnex, { Knex } from './lib/sql/CustomKnex'; -export {XKnex, Knex}; +export { XKnex, Knex }; /** * @copyright Copyright (c) 2021, Xgene Cloud Ltd * diff --git a/packages/nocodb/src/lib/dataMapper/lib/BaseModel.ts b/packages/nocodb/src/lib/dataMapper/lib/BaseModel.ts index eadde4f810..f29ae30d7b 100644 --- a/packages/nocodb/src/lib/dataMapper/lib/BaseModel.ts +++ b/packages/nocodb/src/lib/dataMapper/lib/BaseModel.ts @@ -1,11 +1,10 @@ /* eslint-disable @typescript-eslint/ban-types,prefer-const */ -import Knex from "knex"; +import Knex from 'knex'; const autoBind = require('auto-bind'); const _ = require('lodash'); const Validator = require('validator'); - // interface BaseModel { // beforeInsert(data): Promise; // @@ -55,7 +54,6 @@ const Validator = require('validator'); * */ abstract class BaseModel { - protected dbDriver: Knex; protected columns: any[]; protected pks: any[]; @@ -68,7 +66,6 @@ abstract class BaseModel { public readonly type: string; public readonly tn: string; - /** * * @param {Object} args @@ -83,14 +80,13 @@ abstract class BaseModel { * */ constructor({ - dbDriver, - tn, - columns, - hasMany = [], - belongsTo = [], - type = 'table' - }) { - + dbDriver, + tn, + columns, + hasMany = [], + belongsTo = [], + type = 'table' + }) { this.dbDriver = dbDriver; this.tn = tn; this.columns = columns; @@ -116,10 +112,9 @@ abstract class BaseModel { this.clientType = this.dbDriver.clientType(); - autoBind(this) + autoBind(this); } - /** * Validates column values against validation functions * @@ -131,12 +126,19 @@ abstract class BaseModel { async validate(columns) { // let cols = Object.keys(this.columns); for (let i = 0; i < this.columns.length; ++i) { - const {validate: {func, msg}, cn} = this.columns[i]; + const { + validate: { func, msg }, + cn + } = this.columns[i]; for (let j = 0; j < func.length; ++j) { const fn = typeof func[j] === 'string' ? Validator[func[j]] : func[j]; - const arg = typeof func[j] === 'string' ? columns[cn] + "" : columns[cn]; - if (cn in columns && !(fn.constructor.name === "AsyncFunction" ? await fn(arg) : fn(arg))) - throw new Error(msg[j]) + const arg = + typeof func[j] === 'string' ? columns[cn] + '' : columns[cn]; + if ( + cn in columns && + !(fn.constructor.name === 'AsyncFunction' ? await fn(arg) : fn(arg)) + ) + throw new Error(msg[j]); } } return true; @@ -175,9 +177,9 @@ abstract class BaseModel { * @returns {Object} - foreign key where condition * @private */ - _whereFk({tnp, parentId}) { - const {rcn} = this.belongsToRelations.find(({rtn}) => rtn === tnp) - const where = {[rcn]: parentId}; + _whereFk({ tnp, parentId }) { + const { rcn } = this.belongsToRelations.find(({ rtn }) => rtn === tnp); + const where = { [rcn]: parentId }; return where; } @@ -197,7 +199,6 @@ abstract class BaseModel { return objCopy; } - /** * Creates row in table * @@ -206,11 +207,9 @@ abstract class BaseModel { */ // @ts-ignore async insert(data, trx?: any, cookie?: any) { - try { - if ('beforeInsert' in this) { - await this.beforeInsert(data, trx, cookie) + await this.beforeInsert(data, trx, cookie); } let response; @@ -219,7 +218,10 @@ abstract class BaseModel { const query = this.$db.insert(data); - if (this.dbDriver.clientType() === 'pg' || this.dbDriver.clientType() === 'mssql') { + if ( + this.dbDriver.clientType() === 'pg' || + this.dbDriver.clientType() === 'mssql' + ) { query.returning('*'); response = await this._run(query); } else { @@ -234,12 +236,11 @@ abstract class BaseModel { return response; } catch (e) { console.log(e); - await this.errorInsert(e, data, trx, cookie) + await this.errorInsert(e, data, trx, cookie); throw e; } } - /** * Creates row in this table under a certain parent * @@ -251,20 +252,21 @@ abstract class BaseModel { * @todo should return inserted record */ // @ts-ignore - async insertByFk({parentId, tnp, data}, trx?: any, cookie ?: any) { - + async insertByFk({ parentId, tnp, data }, trx?: any, cookie?: any) { try { - await this.beforeInsert(data, trx, cookie); let response; await this.validate(data); - Object.assign(data, this._whereFk({parentId, tnp})) + Object.assign(data, this._whereFk({ parentId, tnp })); const query = this.$db.insert(data); - if (this.dbDriver.clientType() === 'pg' || this.dbDriver.clientType() === 'mssql') { + if ( + this.dbDriver.clientType() === 'pg' || + this.dbDriver.clientType() === 'mssql' + ) { query.returning('*'); response = await this._run(query); } else { @@ -276,11 +278,11 @@ abstract class BaseModel { } } - await this.afterInsert(data, trx, cookie) + await this.afterInsert(data, trx, cookie); return response; } catch (e) { console.log(e); - await this.errorInsert(e, data, trx, cookie) + await this.errorInsert(e, data, trx, cookie); throw e; } } @@ -292,23 +294,20 @@ abstract class BaseModel { * @returns {Promise<*>} */ async insertb(data) { - try { - - await this.beforeInsertb(data) - + await this.beforeInsertb(data); for (const d of data) { await this.validate(d); } - const response = await this.dbDriver.batchInsert(this.tn, data, 50) + const response = await this.dbDriver + .batchInsert(this.tn, data, 50) .returning(this.pks?.[0]?.cn || '*'); await this.afterInsertb(data); return response; - } catch (e) { await this.errorInsertb(e, data); throw e; @@ -323,9 +322,14 @@ abstract class BaseModel { * @returns {Object} Table row data */ // @ts-ignore - async readByPk(id, {conditionGraph}) { + async readByPk(id, { conditionGraph }) { try { - return await this._run(this.$db.select().where(this._wherePk(id)).first()); + return await this._run( + this.$db + .select() + .where(this._wherePk(id)) + .first() + ); } catch (e) { console.log(e); throw e; @@ -341,13 +345,20 @@ abstract class BaseModel { * @param {String} args.tnp - parent table name * @returns {Promise} returns row */ - async readByFk({id, parentId, tnp}) { + async readByFk({ id, parentId, tnp }) { try { - - return await this._run(this.$db.select().where(this._wherePk(id)).andWhere(this._whereFk({ - tnp, - parentId - })).limit(1)); + return await this._run( + this.$db + .select() + .where(this._wherePk(id)) + .andWhere( + this._whereFk({ + tnp, + parentId + }) + ) + .limit(1) + ); } catch (e) { console.log(e); throw e; @@ -368,19 +379,24 @@ abstract class BaseModel { * @throws {Error} */ async list(args) { - try { + const { + fields, + where, + limit, + offset, + sort, + condition + } = this._getListArgs(args); - const {fields, where, limit, offset, sort, condition} = this._getListArgs(args); - - const query = this.$db.select(...fields.split(',')) + const query = this.$db + .select(...fields.split(',')) .xwhere(where) .condition(condition); - this._paginateAndSort(query, {limit, offset, sort}); + this._paginateAndSort(query, { limit, offset, sort }); return await this._run(query); - } catch (e) { console.log(e); throw e; @@ -402,10 +418,13 @@ abstract class BaseModel { */ async findOne(args) { try { - const {fields, where, condition} = this._getListArgs(args); - const query = this.$db.select(fields) - .xwhere(where).condition(condition).first(); - this._paginateAndSort(query, args) + const { fields, where, condition } = this._getListArgs(args); + const query = this.$db + .select(fields) + .xwhere(where) + .condition(condition) + .first(); + this._paginateAndSort(query, args); return await this._run(query); } catch (e) { console.log(e); @@ -428,13 +447,16 @@ abstract class BaseModel { * @memberof BaseModel * @throws {Error} */ - async findOneByFk({parentId, tnp, ...args}) { + async findOneByFk({ parentId, tnp, ...args }) { try { - const {fields, where, condition} = this._getListArgs(args); - const query = this.$db.select(fields) - .where(this._whereFk({parentId, tnp})) - .xwhere(where).condition(condition).first(); - this._paginateAndSort(query, args) + const { fields, where, condition } = this._getListArgs(args); + const query = this.$db + .select(fields) + .where(this._whereFk({ parentId, tnp })) + .xwhere(where) + .condition(condition) + .first(); + this._paginateAndSort(query, args); return await this._run(query); } catch (e) { console.log(e); @@ -442,7 +464,6 @@ abstract class BaseModel { } } - /** * Get the count of rows based on the where * @@ -452,19 +473,21 @@ abstract class BaseModel { * @memberof BaseModel * @throws {Error} */ - async countByPk({where, condition}) { + async countByPk({ where, condition }) { try { - return await this._run(this.$db.count(`${(this.pks?.[0] || this.columns[0]).cn} as count`) - .xwhere(where) - .condition(condition) - .first()); + return await this._run( + this.$db + .count(`${(this.pks?.[0] || this.columns[0]).cn} as count`) + .xwhere(where) + .condition(condition) + .first() + ); } catch (e) { console.log(e); throw e; } } - /** * Get the count of rows based on the where * @@ -476,15 +499,21 @@ abstract class BaseModel { * @memberof BaseModel * @throws {Error} */ - async countByFk({where, parentId, tnp, condition}) { + async countByFk({ where, parentId, tnp, condition }) { try { - return await this._run(this.$db.where(this._whereFk({ - parentId, - tnp - })).count(`${(this.pks?.[0] || this.columns[0]).cn} as count`) - .xwhere(where) - .condition(condition) - .first()); + return await this._run( + this.$db + .where( + this._whereFk({ + parentId, + tnp + }) + ) + .count(`${(this.pks?.[0] || this.columns[0]).cn} as count`) + .xwhere(where) + .condition(condition) + .first() + ); } catch (e) { console.log(e); throw e; @@ -499,7 +528,7 @@ abstract class BaseModel { * @returns {Number} 1 for success, 0 for failure */ // @ts-ignore - async updateByPk(id, data, trx?: any, cookie ?: any) { + async updateByPk(id, data, trx?: any, cookie?: any) { try { await this.beforeUpdate(data, trx, cookie); @@ -507,7 +536,9 @@ abstract class BaseModel { let response; // this.validate(data); - response = await this._run(this.$db.update(data).where(this._wherePk(id))); + response = await this._run( + this.$db.update(data).where(this._wherePk(id)) + ); await this.afterUpdate(data, trx, cookie); return response; } catch (e) { @@ -528,18 +559,25 @@ abstract class BaseModel { * @returns {Number} 1 for success, 0 for failure */ // @ts-ignore - async updateByFk({id, parentId, tnp, data}, trx?: any, cookie ?: any) { + async updateByFk({ id, parentId, tnp, data }, trx?: any, cookie?: any) { try { - await this.beforeUpdate({id, parentId, tnp, data}, trx, cookie); + await this.beforeUpdate({ id, parentId, tnp, data }, trx, cookie); await this.validate(data); let response; // this.validate(data); - response = await this._run(this.$db.update(data).where(this._wherePk(id)).andWhere(this._whereFk({ - tnp, - parentId - }))); + response = await this._run( + this.$db + .update(data) + .where(this._wherePk(id)) + .andWhere( + this._whereFk({ + tnp, + parentId + }) + ) + ); await this.afterUpdate(response, trx, cookie); return response; } catch (e) { @@ -549,7 +587,6 @@ abstract class BaseModel { } } - /** * Delete table row data by primary key * @@ -573,7 +610,6 @@ abstract class BaseModel { } } - /** * Delete table row data by primary key and foreign key * @@ -584,21 +620,28 @@ abstract class BaseModel { * @returns {Number} 1 for success, 0 for failure */ // @ts-ignore - async delByFk({id, parentId, tnp}, trx?: any, cookie ?: any) { + async delByFk({ id, parentId, tnp }, trx?: any, cookie?: any) { try { - await this.beforeDelete({id, parentId, tnp}, trx, cookie); + await this.beforeDelete({ id, parentId, tnp }, trx, cookie); let response; - response = await this._run(this.$db.del().where(this._wherePk(id)).andWhere(this._whereFk({ - tnp, - parentId - }))); + response = await this._run( + this.$db + .del() + .where(this._wherePk(id)) + .andWhere( + this._whereFk({ + tnp, + parentId + }) + ) + ); await this.afterDelete(response, trx, cookie); return response; } catch (e) { console.log(e); - await this.errorDelete(e, {id, parentId, tnp}, trx, cookie); + await this.errorDelete(e, { id, parentId, tnp }, trx, cookie); throw e; } } @@ -610,10 +653,8 @@ abstract class BaseModel { * @returns {Promise} - 1 for success, 0 for failure */ async updateb(data) { - let trx; try { - await this.beforeUpdateb(data); trx = await this.dbDriver.transaction(); @@ -621,7 +662,11 @@ abstract class BaseModel { const res = []; for (const d of data) { // this.validate(d); - const response = await this._run(trx(this.tn).update(d).where(this._extractPks(d))); + const response = await this._run( + trx(this.tn) + .update(d) + .where(this._extractPks(d)) + ); res.push(response); } @@ -629,17 +674,14 @@ abstract class BaseModel { await this.afterUpdateb(res); return res; - } catch (e) { - if (trx) - trx.rollback(); + if (trx) trx.rollback(); console.log(e); await this.errorUpdateb(e, data); throw e; } } - /** * Bulk delete happens within a transaction * @@ -654,7 +696,11 @@ abstract class BaseModel { const res = []; for (const d of ids) { - const response = await this._run(trx(this.tn).del().where(this._extractPks(d))); + const response = await this._run( + trx(this.tn) + .del() + .where(this._extractPks(d)) + ); res.push(response); } trx.commit(); @@ -662,15 +708,12 @@ abstract class BaseModel { await this.afterDeleteb(res); return res; - } catch (e) { - if (trx) - trx.rollback(); + if (trx) trx.rollback(); console.log(e); await this.errorDeleteb(e, ids); throw e; } - } /** @@ -681,7 +724,10 @@ abstract class BaseModel { */ async exists(id, _) { try { - return Object.keys((await this.readByPk(id, {conditionGraph: null}))).length !== 0; + return ( + Object.keys(await this.readByPk(id, { conditionGraph: null })) + .length !== 0 + ); } catch (e) { console.log(e); throw e; @@ -694,9 +740,9 @@ abstract class BaseModel { * @param {String} id - ___ separated primary key string * @returns {Promise} - true for exits and false for none */ - async existsByFk({id, parentId, tnp}) { + async existsByFk({ id, parentId, tnp }) { try { - return (await this.readByFk({id, parentId, tnp})).length !== 0; + return (await this.readByFk({ id, parentId, tnp })).length !== 0; } catch (e) { console.log(e); throw e; @@ -717,16 +763,19 @@ abstract class BaseModel { * @memberof BaseModel * @throws {Error} */ - async groupBy({having, fields = '', column_name, limit, offset, sort}) { + async groupBy({ having, fields = '', column_name, limit, offset, sort }) { try { - const columns = [...(column_name ? [column_name] : []), ...fields.split(',').filter(Boolean)]; + const columns = [ + ...(column_name ? [column_name] : []), + ...fields.split(',').filter(Boolean) + ]; const query = this.$db .groupBy(columns) .count(`${(this.pks?.[0] || this.columns[0]).cn} as count`) .select(columns) .xhaving(having); - this._paginateAndSort(query, {limit, offset, sort}); + this._paginateAndSort(query, { limit, offset, sort }); return await this._run(query); } catch (e) { @@ -735,7 +784,6 @@ abstract class BaseModel { } } - /** * Get the rows by aggregation by an aggregation function(s) * @@ -751,21 +799,26 @@ abstract class BaseModel { * @memberof BaseModel * @throws {Error} */ - async aggregate({having, fields = '', func, column_name, limit, offset, sort}) { + async aggregate({ + having, + fields = '', + func, + column_name, + limit, + offset, + sort + }) { try { - const query = this.$db - .select(...fields.split(',')) - .xhaving(having); + const query = this.$db.select(...fields.split(',')).xhaving(having); if (fields) { - query.groupBy(...fields.split(',')) + query.groupBy(...fields.split(',')); } if (func && column_name) { - func.split(',').forEach(fn => query[fn](`${column_name} as ${fn}`)) + func.split(',').forEach(fn => query[fn](`${column_name} as ${fn}`)); } - - this._paginateAndSort(query, {limit, offset, sort}); + this._paginateAndSort(query, { limit, offset, sort }); return await this._run(query); } catch (e) { @@ -774,7 +827,6 @@ abstract class BaseModel { } } - /** * Distribution of column values in the table * @@ -803,8 +855,7 @@ abstract class BaseModel { * @memberof BaseModel * @throws {Error} */ - async distribution({column_name, steps, func = 'count', min, max, step}) { - + async distribution({ column_name, steps, func = 'count', min, max, step }) { try { const ranges = []; @@ -814,37 +865,42 @@ abstract class BaseModel { step = +step; for (let i = 0; i < max / step; i++) { - ranges.push([i * step + (i && 1), Math.min((i + 1) * step, max)]) + ranges.push([i * step + (i && 1), Math.min((i + 1) * step, max)]); } }; - if (!isNaN(+min) && !isNaN(+max) && !isNaN(+step)) { - generateWindows(ranges, min, max, step) + generateWindows(ranges, min, max, step); } else if (steps) { const splitArr = steps.split(','); for (let i = 0; i < splitArr.length - 1; i++) { - ranges.push([+splitArr[i] + (i ? 1 : 0), splitArr[i + 1]]) + ranges.push([+splitArr[i] + (i ? 1 : 0), splitArr[i + 1]]); } } else { - const {min, max, step} = await this.$db + const { min, max, step } = await this.$db .min(`${column_name} as min`) .max(`${column_name} as max`) .avg(`${column_name} as step`) .first(); - generateWindows(ranges, min, max, Math.round(step)) + generateWindows(ranges, min, max, Math.round(step)); } - return (await this.dbDriver.unionAll( - ranges.map(([start, end]) => { - const query = this.$db.xwhere(`(${column_name},ge,${start})~and(${column_name},le,${end})`); + return ( + await this.dbDriver.unionAll( + ranges.map(([start, end]) => { + const query = this.$db.xwhere( + `(${column_name},ge,${start})~and(${column_name},le,${end})` + ); if (func) { - func.split(',').forEach(fn => query[fn](`${column_name} as ${fn}`)) + func + .split(',') + .forEach(fn => query[fn](`${column_name} as ${fn}`)); } return this.isSqlite() ? this.dbDriver.select().from(query) : query; - } - ), !this.isSqlite() - )).map((row, i) => { + }), + !this.isSqlite() + ) + ).map((row, i) => { row.range = ranges[i].join('-'); return row; }); @@ -852,10 +908,8 @@ abstract class BaseModel { console.log(e); throw e; } - } - /** * Get the list of distinct rows * @@ -870,12 +924,12 @@ abstract class BaseModel { * @memberof BaseModel * @throws {Error} */ - async distinct({cn, fields = '', where, limit, offset, sort, condition}) { + async distinct({ cn, fields = '', where, limit, offset, sort, condition }) { try { const query = this.$db; query.distinct(cn, ...fields.split(',').filter(Boolean)); query.xwhere(where).condition(condition); - this._paginateAndSort(query, {limit, offset, sort}); + this._paginateAndSort(query, { limit, offset, sort }); return await this._run(query); } catch (e) { console.log(e); @@ -900,7 +954,6 @@ abstract class BaseModel { } } - /** * Get child list and map to input parent * @@ -911,32 +964,41 @@ abstract class BaseModel { * @returns {Promise} * @private */ - async _getChildListInParent({parent, child}, rest = {}, index) { - let {fields, where, limit, offset, sort, condition} = this._getChildListArgs(rest, index); - const {cn} = this.hasManyRelations.find(({tn}) => tn === child) || {}; + async _getChildListInParent({ parent, child }, rest = {}, index) { + let { + fields, + where, + limit, + offset, + sort, + condition + } = this._getChildListArgs(rest, index); + const { cn } = this.hasManyRelations.find(({ tn }) => tn === child) || {}; if (fields !== '*' && fields.split(',').indexOf(cn) === -1) { fields += ',' + cn; } + const childs = await this._run( + this.dbDriver.union( + parent.map(p => { + const query = this.dbDriver(child) + .where({ [cn]: p[this.pks?.[0]?.cn] }) + .xwhere(where) + .condition(condition) + .select(...fields.split(',')); - const childs = await this._run(this.dbDriver.union( - parent.map(p => { - const query = this - .dbDriver(child) - .where({[cn]: p[this.pks?.[0]?.cn]}) - .xwhere(where).condition(condition) - .select(...fields.split(',')); - - this._paginateAndSort(query, {sort, limit, offset}); - return this.isSqlite() ? this.dbDriver.select().from(query) : query; - }), !this.isSqlite() - )); + this._paginateAndSort(query, { sort, limit, offset }); + return this.isSqlite() ? this.dbDriver.select().from(query) : query; + }), + !this.isSqlite() + ) + ); const gs = _.groupBy(childs, cn); parent.forEach(row => { row[child] = gs[row[this.pks?.[0]?.cn]] || []; - }) + }); } /** @@ -952,16 +1014,26 @@ abstract class BaseModel { * @param {String} [args.sort] - comma separated column names where each column name is cn ascending and -cn is cn descending * @returns {Promise} return child rows */ - async hasManyChildren({child, parentId, ...args}) { + async hasManyChildren({ child, parentId, ...args }) { try { - const {fields, where, limit, offset, sort, condition} = this._getListArgs(args); - const {rcn} = this.hasManyRelations.find(({tn}) => tn === child) || {}; - - const query = this.dbDriver(child).select(...fields.split(',')) + const { + fields, + where, + limit, + offset, + sort, + condition + } = this._getListArgs(args); + const { rcn } = + this.hasManyRelations.find(({ tn }) => tn === child) || {}; + + const query = this.dbDriver(child) + .select(...fields.split(',')) .where(rcn, parentId) - .xwhere(where).condition(condition); + .xwhere(where) + .condition(condition); - this._paginateAndSort(query, {limit, offset, sort}); + this._paginateAndSort(query, { limit, offset, sort }); return await this._run(query); } catch (e) { console.log(e); @@ -986,20 +1058,30 @@ abstract class BaseModel { * @param {String} [args.sort*] - comma separated column names where each column name is cn ascending and -cn is cn descending(* is a natural number 'i' where i is index of child table in comma separated list) * @returns {Promise} */ - async hasManyList({childs, where, fields, f, ...rest}) { + async hasManyList({ childs, where, fields, f, ...rest }) { fields = fields || f || '*'; try { - - if (fields !== '*' && fields.split(',').indexOf(this.pks?.[0]?.cn) === -1) { + if ( + fields !== '*' && + fields.split(',').indexOf(this.pks?.[0]?.cn) === -1 + ) { fields += ',' + this.pks?.[0]?.cn; } - const parent = await this.list({childs, where, fields, ...rest}); + const parent = await this.list({ childs, where, fields, ...rest }); if (parent && parent.length) - await Promise.all([...new Set(childs.split('.'))].map((child, index) => this._getChildListInParent({ - parent, - child - }, rest, index))); + await Promise.all( + [...new Set(childs.split('.'))].map((child, index) => + this._getChildListInParent( + { + parent, + child + }, + rest, + index + ) + ) + ); return parent; } catch (e) { console.log(e); @@ -1020,25 +1102,30 @@ abstract class BaseModel { * @param {String} [args.sort] - comma separated column names where each column name is cn ascending and -cn is cn descending * @returns {Promise} */ - async belongsTo({parents, where, fields, f, ...rest}) { + async belongsTo({ parents, where, fields, f, ...rest }) { fields = fields || f || '*'; try { - for (const parent of parents.split('~')) { - const {cn} = this.belongsToRelations.find(({rtn}) => rtn === parent) || {}; + const { cn } = + this.belongsToRelations.find(({ rtn }) => rtn === parent) || {}; if (fields !== '*' && fields.split(',').indexOf(cn) === -1) { fields += ',' + cn; } } - const childs = await this.list({where, fields, ...rest}); - + const childs = await this.list({ where, fields, ...rest }); - await Promise.all(parents.split('~').map((parent, index) => { - const {cn, rcn} = this.belongsToRelations.find(({rtn}) => rtn === parent) || {}; - const parentIds = [...new Set(childs.map(c => c[cn]))]; - return this._belongsTo({parent, rcn, parentIds, childs, cn, ...rest}, index); - })) + await Promise.all( + parents.split('~').map((parent, index) => { + const { cn, rcn } = + this.belongsToRelations.find(({ rtn }) => rtn === parent) || {}; + const parentIds = [...new Set(childs.map(c => c[cn]))]; + return this._belongsTo( + { parent, rcn, parentIds, childs, cn, ...rest }, + index + ); + }) + ); return childs; } catch (e) { @@ -1047,7 +1134,6 @@ abstract class BaseModel { } } - /** * Get parent and map to input child * @@ -1059,19 +1145,23 @@ abstract class BaseModel { * @returns {Promise} * @private */ - async _belongsTo({parent, rcn, parentIds, childs, cn, ...rest}, index) { - let {fields} = this._getChildListArgs(rest, index); + async _belongsTo({ parent, rcn, parentIds, childs, cn, ...rest }, index) { + let { fields } = this._getChildListArgs(rest, index); if (fields !== '*' && fields.split(',').indexOf(rcn) === -1) { fields += ',' + rcn; } - const parents = await this._run(this.dbDriver(parent).select(...fields.split(',')).whereIn(rcn, parentIds)); + const parents = await this._run( + this.dbDriver(parent) + .select(...fields.split(',')) + .whereIn(rcn, parentIds) + ); const gs = _.groupBy(parents, rcn); childs.forEach(row => { row[parent] = gs[row[cn]] && gs[row[cn]][0]; - }) + }); } /** @@ -1086,32 +1176,40 @@ abstract class BaseModel { * @param {String} [args.sort] - comma separated column names where each column name is cn ascending and -cn is cn descending * @returns {Promise>} key will be parent pk and value will be child list */ - async hasManyListGQL({child, ids, ...rest}) { + async hasManyListGQL({ child, ids, ...rest }) { try { - let {fields, where, limit, offset, sort, condition} = this._getChildListArgs(rest); + let { + fields, + where, + limit, + offset, + sort, + condition + } = this._getChildListArgs(rest); - const {cn} = this.hasManyRelations.find(({tn}) => tn === child) || {}; + const { cn } = this.hasManyRelations.find(({ tn }) => tn === child) || {}; if (fields !== '*' && fields.split(',').indexOf(cn) === -1) { fields += ',' + cn; } - const childs = await this._run(this.dbDriver.union( - ids.map(p => { - const query = this - .dbDriver(child) - .where({[cn]: p}) - .xwhere(where).condition(condition) - .select(...fields.split(',')); - - this._paginateAndSort(query, {sort, limit, offset}); - return this.isSqlite() ? this.dbDriver.select().from(query) : query; - }), !this.isSqlite() - )); + const childs = await this._run( + this.dbDriver.union( + ids.map(p => { + const query = this.dbDriver(child) + .where({ [cn]: p }) + .xwhere(where) + .condition(condition) + .select(...fields.split(',')); + this._paginateAndSort(query, { sort, limit, offset }); + return this.isSqlite() ? this.dbDriver.select().from(query) : query; + }), + !this.isSqlite() + ) + ); return _.groupBy(childs, cn); - } catch (e) { console.log(e); throw e; @@ -1134,28 +1232,29 @@ abstract class BaseModel { * @param {String} [args.sort] - comma separated column names where each column name is cn ascending and -cn is cn descending * @returns {Promise>} key will be parent pk and value will be child list */ - async hasManyListCount({child, ids, ...rest}) { + async hasManyListCount({ child, ids, ...rest }) { try { - const {where, condition} = this._getChildListArgs(rest); - - const {cn} = this.hasManyRelations.find(({tn}) => tn === child) || {}; - - const childs = await this._run(this.dbDriver.unionAll( - ids.map(p => { - const query = this - .dbDriver(child) - .where({[cn]: p}) - .xwhere(where) - .condition(condition) - .count(`${cn} as count`) - .first(); - return this.isSqlite() ? this.dbDriver.select().from(query) : query; - }), !this.isSqlite() - )); + const { where, condition } = this._getChildListArgs(rest); + + const { cn } = this.hasManyRelations.find(({ tn }) => tn === child) || {}; + + const childs = await this._run( + this.dbDriver.unionAll( + ids.map(p => { + const query = this.dbDriver(child) + .where({ [cn]: p }) + .xwhere(where) + .condition(condition) + .count(`${cn} as count`) + .first(); + return this.isSqlite() ? this.dbDriver.select().from(query) : query; + }), + !this.isSqlite() + ) + ); - return childs.map(({count}) => count); + return childs.map(({ count }) => count); // return _.groupBy(childs, cn); - } catch (e) { console.log(e); throw e; @@ -1173,18 +1272,24 @@ abstract class BaseModel { * @returns {Object} query appended with paginate and sort params * @private */ - _paginateAndSort(query, {limit = 20, offset = 0, sort = ''}: { limit?: number | string, offset?: number | string, sort?: string }) { - query.offset(offset) - .limit(limit); + _paginateAndSort( + query, + { + limit = 20, + offset = 0, + sort = '' + }: { limit?: number | string; offset?: number | string; sort?: string } + ) { + query.offset(offset).limit(limit); if (sort) { sort.split(',').forEach(o => { if (o[0] === '-') { - query.orderBy(o.slice(1), 'desc') + query.orderBy(o.slice(1), 'desc'); } else { - query.orderBy(o, 'asc') + query.orderBy(o, 'asc'); } - }) + }); } return query; } @@ -1222,7 +1327,13 @@ abstract class BaseModel { _getListArgs(args) { const obj: XcFilter = {}; obj.where = args.where || args.w || ''; - obj.limit = Math.max(Math.min(args.limit || args.l || this.config.limitDefault, this.config.limitMax), this.config.limitMin); + obj.limit = Math.max( + Math.min( + args.limit || args.l || this.config.limitDefault, + this.config.limitMax + ), + this.config.limitMin + ); obj.offset = args.offset || args.o || 0; obj.fields = args.fields || args.f || '*'; obj.sort = args.sort || args.s; @@ -1241,14 +1352,19 @@ abstract class BaseModel { index++; const obj: XcFilter = {}; obj.where = args[`where${index}`] || args[`w${index}`] || ''; - obj.limit = Math.max(Math.min(args[`limit${index}`] || args[`l${index}`] || this.config.limitDefault, this.config.limitMax), this.config.limitMin); + obj.limit = Math.max( + Math.min( + args[`limit${index}`] || args[`l${index}`] || this.config.limitDefault, + this.config.limitMax + ), + this.config.limitMin + ); obj.offset = args[`offset${index}`] || args[`o${index}`] || 0; obj.fields = args[`fields${index}`] || args[`f${index}`] || '*'; obj.sort = args[`sort${index}`] || args[`s${index}`]; return obj; } - /** * Before Insert is a hook which can be override in subclass * @abstract @@ -1256,10 +1372,7 @@ abstract class BaseModel { * @param {Object} trx? - knex transaction reference */ // @ts-ignore - async beforeInsert(data, trx?: any, cookie?: {}) { - - } - + async beforeInsert(data, trx?: any, cookie?: {}) {} /** * After Insert is a hook which can be override in subclass @@ -1268,10 +1381,7 @@ abstract class BaseModel { * @param {Object} trx? - knex transaction reference */ // @ts-ignore - async afterInsert(response, trx?: any, cookie?: {}) { - - } - + async afterInsert(response, trx?: any, cookie?: {}) {} /** * After Insert is a hook which can be override in subclass @@ -1281,9 +1391,7 @@ abstract class BaseModel { * @param {Object} trx? - knex transaction reference */ // @ts-ignore - async errorInsert(err, data, trx?: any, cookie?: {}) { - - } + async errorInsert(err, data, trx?: any, cookie?: {}) {} /** * Before Update is a hook which can be override in subclass @@ -1292,10 +1400,7 @@ abstract class BaseModel { * @param {Object} trx? - knex transaction reference */ // @ts-ignore - async beforeUpdate(data, trx?: any, cookie?: {}) { - - } - + async beforeUpdate(data, trx?: any, cookie?: {}) {} /** * After Update is a hook which can be override in subclass @@ -1304,10 +1409,7 @@ abstract class BaseModel { * @param {Object} trx? - knex transaction reference */ // @ts-ignore - async afterUpdate(response, trx?: any, cookie?: {}) { - - } - + async afterUpdate(response, trx?: any, cookie?: {}) {} /** * Error update is a hook which can be override in subclass @@ -1317,10 +1419,7 @@ abstract class BaseModel { * @param {Object} trx? - knex transaction reference */ // @ts-ignore - async errorUpdate(err, data, trx?: any, cookie?: {}) { - - } - + async errorUpdate(err, data, trx?: any, cookie?: {}) {} /** * Before delete is a hook which can be override in subclass @@ -1329,9 +1428,7 @@ abstract class BaseModel { * @param {Object} trx? - knex transaction reference */ // @ts-ignore - async beforeDelete(data, trx?: any, cookie?: {}) { - - } + async beforeDelete(data, trx?: any, cookie?: {}) {} /** * After Delete is a hook which can be override in subclass @@ -1340,10 +1437,7 @@ abstract class BaseModel { * @param {Object} trx? - knex transaction reference */ // @ts-ignore - async afterDelete(response, trx?: any, cookie?: {}) { - - } - + async afterDelete(response, trx?: any, cookie?: {}) {} /** * Error delete is a hook which can be override in subclass @@ -1353,10 +1447,7 @@ abstract class BaseModel { * @param {Object} trx? - knex transaction reference */ // @ts-ignore - async errorDelete(err, data, trx?: any, cookie?: {}) { - - } - + async errorDelete(err, data, trx?: any, cookie?: {}) {} /** * Before insert bulk is a hook which can be override in subclass @@ -1365,9 +1456,7 @@ abstract class BaseModel { * @param {Object} trx - knex transaction reference */ // @ts-ignore - async beforeInsertb(data, trx?: any) { - - } + async beforeInsertb(data, trx?: any) {} /** * After insert bulk is a hook which can be override in subclass @@ -1376,9 +1465,7 @@ abstract class BaseModel { * @param {Object} trx - knex transaction reference */ // @ts-ignore - async afterInsertb(response, trx?: any) { - - } + async afterInsertb(response, trx?: any) {} /** * Error insert bulk is a hook which can be override in subclass @@ -1388,9 +1475,7 @@ abstract class BaseModel { * @param {Object} trx - knex transaction reference */ // @ts-ignore - async errorInsertb(err, data, trx?: any) { - - } + async errorInsertb(err, data, trx?: any) {} /** * Before update bulk is a hook which can be override in subclass @@ -1399,9 +1484,7 @@ abstract class BaseModel { * @param {Object} trx - knex transaction reference */ // @ts-ignore - async beforeUpdateb(data, trx?: any) { - - } + async beforeUpdateb(data, trx?: any) {} /** * After update bulk is a hook which can be override in subclass @@ -1410,9 +1493,7 @@ abstract class BaseModel { * @param {Object} trx - knex transaction reference */ // @ts-ignore - async afterUpdateb(response, trx?: any) { - - } + async afterUpdateb(response, trx?: any) {} /** * Error update bulk is a hook which can be override in subclass @@ -1422,9 +1503,7 @@ abstract class BaseModel { * @param {Object} trx - knex transaction reference */ // @ts-ignore - async errorUpdateb(err, data, trx?: any) { - - } + async errorUpdateb(err, data, trx?: any) {} /** * Before delete bulk is a hook which can be override in subclass @@ -1433,9 +1512,7 @@ abstract class BaseModel { * @param {Object} trx - knex transaction reference */ // @ts-ignore - async beforeDeleteb(data, trx?: any) { - - } + async beforeDeleteb(data, trx?: any) {} /** * After delete bulk is a hook which can be override in subclass @@ -1444,10 +1521,7 @@ abstract class BaseModel { * @param {Object} trx - knex transaction reference */ // @ts-ignore - async afterDeleteb(response, trx?: any) { - - } - + async afterDeleteb(response, trx?: any) {} /** * Error delete bulk is a hook which can be override in subclass @@ -1457,14 +1531,9 @@ abstract class BaseModel { * @param {Object} trx - knex transaction reference */ // @ts-ignore - async errorDeleteb(err, data, trx?: any) { - - } - - + async errorDeleteb(err, data, trx?: any) {} } - export interface XcFilter { where?: string; having?: string; @@ -1486,7 +1555,6 @@ export interface XcFilterWithAlias extends XcFilter { f?: string; } - export default BaseModel; /** * @copyright Copyright (c) 2021, Xgene Cloud Ltd diff --git a/packages/nocodb/src/lib/dataMapper/lib/DbFactory.ts b/packages/nocodb/src/lib/dataMapper/lib/DbFactory.ts index 55b5fc7294..6ac30d73c2 100644 --- a/packages/nocodb/src/lib/dataMapper/lib/DbFactory.ts +++ b/packages/nocodb/src/lib/dataMapper/lib/DbFactory.ts @@ -2,12 +2,14 @@ import knex from './sql/CustomKnex'; export class DbFactory { static create(connectionConfig) { - if (connectionConfig.client === "sqlite3") { - return knex(connectionConfig.connection) - } else if (['mysql', 'mysql2', 'pg', 'mssql'].includes(connectionConfig.client)) { - return knex(connectionConfig) + if (connectionConfig.client === 'sqlite3') { + return knex(connectionConfig.connection); + } else if ( + ['mysql', 'mysql2', 'pg', 'mssql'].includes(connectionConfig.client) + ) { + return knex(connectionConfig); } - throw new Error("Database not supported"); + throw new Error('Database not supported'); } } /** diff --git a/packages/nocodb/src/lib/dataMapper/lib/sql/CustomKnex.ts b/packages/nocodb/src/lib/dataMapper/lib/sql/CustomKnex.ts index ea0a260d6b..965be227d0 100644 --- a/packages/nocodb/src/lib/dataMapper/lib/sql/CustomKnex.ts +++ b/packages/nocodb/src/lib/dataMapper/lib/sql/CustomKnex.ts @@ -1,6 +1,6 @@ import Knex from 'knex'; -import {BaseModelSql} from "./BaseModelSql"; +import { BaseModelSql } from './BaseModelSql'; const opMapping = { eq: '=', @@ -10,7 +10,7 @@ const opMapping = { ge: '>=', not: '!=', like: 'like' -} +}; /** * Converts a condition string to conditions array @@ -20,7 +20,7 @@ const opMapping = { */ function toArrayOfConditions(str) { if (!str) { - return [] + return []; } let nestedArrayConditions = []; @@ -34,242 +34,394 @@ function toArrayOfConditions(str) { // if it's a simple query simply return array of conditions if (openIndex === -1) { - if (str && str != "~not") nestedArrayConditions = str.split(/(?=~(?:or(?:not)?|and(?:not)?|not)\()/); + if (str && str != '~not') + nestedArrayConditions = str.split( + /(?=~(?:or(?:not)?|and(?:not)?|not)\()/ + ); return nestedArrayConditions || []; } - // iterate until finding right closing - while ((nextOpenIndex = str.substring(0, closingIndex).indexOf('((', nextOpenIndex + 1)) != -1) { - closingIndex = str.indexOf('))', closingIndex + 1) + while ( + (nextOpenIndex = str + .substring(0, closingIndex) + .indexOf('((', nextOpenIndex + 1)) != -1 + ) { + closingIndex = str.indexOf('))', closingIndex + 1); } if (closingIndex === -1) - throw new Error(`${str.substring(0, openIndex + 1).slice(-10)} : Closing bracket not found`) + throw new Error( + `${str + .substring(0, openIndex + 1) + .slice(-10)} : Closing bracket not found` + ); // getting operand starting index const operandStartIndex = str.lastIndexOf('~', openIndex); - const operator = operandStartIndex != -1 ? str.substring(operandStartIndex + 1, openIndex) : ''; + const operator = + operandStartIndex != -1 + ? str.substring(operandStartIndex + 1, openIndex) + : ''; const lhsOfNestedQuery = str.substring(0, openIndex); nestedArrayConditions.push( ...toArrayOfConditions(lhsOfNestedQuery), // calling recursively for nested query - {operator, conditions: toArrayOfConditions(str.substring(openIndex + 1, closingIndex + 1))}, + { + operator, + conditions: toArrayOfConditions( + str.substring(openIndex + 1, closingIndex + 1) + ) + }, // RHS of nested query(recursion) ...toArrayOfConditions(str.substring(closingIndex + 2)) - ) + ); return nestedArrayConditions; } - -const appendWhereCondition = function (conditions, columnAliases: { - [columnAlias: string]: string -}, knexRef, isHaving = false) { +const appendWhereCondition = function( + conditions, + columnAliases: { + [columnAlias: string]: string; + }, + knexRef, + isHaving = false +) { const camKey = isHaving ? 'Having' : 'Where'; const key = isHaving ? 'having' : 'where'; conditions.forEach(condition => { if (Array.isArray(condition)) { - knexRef[key](function () { + knexRef[key](function() { appendWhereCondition(condition, columnAliases, this); - }) + }); } else if (typeof condition === 'object') { - switch (condition.operator) { case 'or': - knexRef[`or${camKey}`](function () { + knexRef[`or${camKey}`](function() { appendWhereCondition(condition.conditions, columnAliases, this); - }) + }); break; case 'and': - knexRef[`and${camKey}`](function () { + knexRef[`and${camKey}`](function() { appendWhereCondition(condition.conditions, columnAliases, this); - }) + }); break; case 'andnot': - knexRef[`and${camKey}Not`](function () { + knexRef[`and${camKey}Not`](function() { appendWhereCondition(condition.conditions, columnAliases, this); - }) + }); break; case 'ornot': - knexRef[`or${camKey}Not`](function () { + knexRef[`or${camKey}Not`](function() { appendWhereCondition(condition.conditions, columnAliases, this); - }) + }); break; case 'not': - knexRef[`${key}Not`](function () { + knexRef[`${key}Not`](function() { appendWhereCondition(condition.conditions, columnAliases, this); - }) + }); break; default: - knexRef[`${key}`](function () { + knexRef[`${key}`](function() { appendWhereCondition(condition.conditions, columnAliases, this); - }) + }); break; } } else if (typeof condition === 'string') { - const matches = condition.match(/^(?:~(\w+))?\(([\w ]+),(\w+),(.*?)\)(?:~(?:or|and|not))?$/) + const matches = condition.match( + /^(?:~(\w+))?\(([\w ]+),(\w+),(.*?)\)(?:~(?:or|and|not))?$/ + ); - if (!matches) throw new Error(`${condition} : not a valid syntax`) + if (!matches) throw new Error(`${condition} : not a valid syntax`); switch (matches[3]) { case 'in': - switch ((matches[1] || '')) { + switch (matches[1] || '') { case 'or': - knexRef[`or${camKey}`](builder => builder[`${key}In`](columnAliases[matches[2]] || matches[2], matches[4].split(','))); + knexRef[`or${camKey}`](builder => + builder[`${key}In`]( + columnAliases[matches[2]] || matches[2], + matches[4].split(',') + ) + ); break; case 'and': - knexRef[`${key}In`](columnAliases[matches[2]] || matches[2], matches[4].split(',')) + knexRef[`${key}In`]( + columnAliases[matches[2]] || matches[2], + matches[4].split(',') + ); break; case 'andnot': - knexRef[`${key}NotIn`](columnAliases[matches[2]] || matches[2], matches[4].split(',')) + knexRef[`${key}NotIn`]( + columnAliases[matches[2]] || matches[2], + matches[4].split(',') + ); break; case 'ornot': - knexRef[`or${camKey}`](builder => builder[`${key}NotIn`](columnAliases[matches[2]] || matches[2], matches[4].split(','))); + knexRef[`or${camKey}`](builder => + builder[`${key}NotIn`]( + columnAliases[matches[2]] || matches[2], + matches[4].split(',') + ) + ); break; case 'not': - knexRef[`${key}NotIn`](columnAliases[matches[2]] || matches[2], matches[4].split(',')) + knexRef[`${key}NotIn`]( + columnAliases[matches[2]] || matches[2], + matches[4].split(',') + ); break; case '': - knexRef[`${key}In`](columnAliases[matches[2]] || matches[2], matches[4].split(',')) + knexRef[`${key}In`]( + columnAliases[matches[2]] || matches[2], + matches[4].split(',') + ); break; default: - throw new Error(`${matches[1]} : Invalid operation.`) + throw new Error(`${matches[1]} : Invalid operation.`); break; } break; case 'is': - if (matches[4] != 'null') throw new Error(`${matches[4]} : not a valid value since 'is' & 'isnot' only supports value null`); - switch ((matches[1] || '')) { + if (matches[4] != 'null') + throw new Error( + `${matches[4]} : not a valid value since 'is' & 'isnot' only supports value null` + ); + switch (matches[1] || '') { case 'or': - knexRef[`or${camKey}`](builder => builder[`${key}Null`](columnAliases[matches[2]] || matches[2])); + knexRef[`or${camKey}`](builder => + builder[`${key}Null`](columnAliases[matches[2]] || matches[2]) + ); break; case 'and': - knexRef[`${key}Null`](columnAliases[matches[2]] || matches[2]) + knexRef[`${key}Null`](columnAliases[matches[2]] || matches[2]); break; case 'andnot': - knexRef[`${key}NotNull`](columnAliases[matches[2]] || matches[2]) + knexRef[`${key}NotNull`](columnAliases[matches[2]] || matches[2]); break; case 'ornot': - knexRef[`or${camKey}`](builder => builder[`${key}NotNull`](columnAliases[matches[2]] || matches[2])); + knexRef[`or${camKey}`](builder => + builder[`${key}NotNull`]( + columnAliases[matches[2]] || matches[2] + ) + ); break; case 'not': - knexRef[`${key}NotNull`](columnAliases[matches[2]] || matches[2]) + knexRef[`${key}NotNull`](columnAliases[matches[2]] || matches[2]); break; case '': - knexRef[`${key}Null`](columnAliases[matches[2]] || matches[2]) + knexRef[`${key}Null`](columnAliases[matches[2]] || matches[2]); break; default: - throw new Error(`${matches[1]} : Invalid operation.`) + throw new Error(`${matches[1]} : Invalid operation.`); break; } break; case 'isnot': - if (matches[4] != 'null') throw new Error(`${matches[4]} : not a valid value since 'is' & 'isnot' only supports value null`); - switch ((matches[1] || '')) { + if (matches[4] != 'null') + throw new Error( + `${matches[4]} : not a valid value since 'is' & 'isnot' only supports value null` + ); + switch (matches[1] || '') { case 'or': - knexRef[`or${camKey}`](builder => builder[`${key}NotNull`](columnAliases[matches[2]] || matches[2])); + knexRef[`or${camKey}`](builder => + builder[`${key}NotNull`]( + columnAliases[matches[2]] || matches[2] + ) + ); break; case 'and': - knexRef[`${key}NotNull`](columnAliases[matches[2]] || matches[2]) + knexRef[`${key}NotNull`](columnAliases[matches[2]] || matches[2]); break; case 'andnot': - knexRef[`${key}NotNull`](columnAliases[matches[2]] || matches[2]) + knexRef[`${key}NotNull`](columnAliases[matches[2]] || matches[2]); break; case 'ornot': - knexRef[`or${camKey}`](builder => builder[`${key}NotNull`](columnAliases[matches[2]] || matches[2])); + knexRef[`or${camKey}`](builder => + builder[`${key}NotNull`]( + columnAliases[matches[2]] || matches[2] + ) + ); break; case 'not': - knexRef[`${key}Null`](columnAliases[matches[2]] || matches[2]) + knexRef[`${key}Null`](columnAliases[matches[2]] || matches[2]); break; case '': - knexRef[`${key}NotNull`](columnAliases[matches[2]] || matches[2]) + knexRef[`${key}NotNull`](columnAliases[matches[2]] || matches[2]); break; default: - throw new Error(`${matches[1]} : Invalid operation.`) + throw new Error(`${matches[1]} : Invalid operation.`); break; } break; - case 'btw': { - const range = matches[4].split(','); - if (range.length !== 2) throw new Error(`${matches[4]} : not a valid value.${range.length > 2 ? ' Between accepts only 2 values' : ' Between requires 2 values'}`); - switch ((matches[1] || '')) { - case 'or': - knexRef[`or${camKey}`](builder => builder[`${key}Between`](columnAliases[matches[2]] || matches[2], range)); - break; - case 'and': - knexRef[`${key}Between`](columnAliases[matches[2]] || matches[2], range) - break; - case 'andnot': - knexRef[`${key}NotBetween`](columnAliases[matches[2]] || matches[2], range) - break; - case 'ornot': - knexRef[`or${camKey}`](builder => builder[`${key}NotBetween`](columnAliases[matches[2]] || matches[2], range)); - break; - case 'not': - knexRef[`${key}NotBetween`](columnAliases[matches[2]] || matches[2], range) - break; - case '': - knexRef[`${key}Between`](columnAliases[matches[2]] || matches[2], range) - break; - default: - throw new Error(`${matches[1]} : Invalid operation.`) - break; + case 'btw': + { + const range = matches[4].split(','); + if (range.length !== 2) + throw new Error( + `${matches[4]} : not a valid value.${ + range.length > 2 + ? ' Between accepts only 2 values' + : ' Between requires 2 values' + }` + ); + switch (matches[1] || '') { + case 'or': + knexRef[`or${camKey}`](builder => + builder[`${key}Between`]( + columnAliases[matches[2]] || matches[2], + range + ) + ); + break; + case 'and': + knexRef[`${key}Between`]( + columnAliases[matches[2]] || matches[2], + range + ); + break; + case 'andnot': + knexRef[`${key}NotBetween`]( + columnAliases[matches[2]] || matches[2], + range + ); + break; + case 'ornot': + knexRef[`or${camKey}`](builder => + builder[`${key}NotBetween`]( + columnAliases[matches[2]] || matches[2], + range + ) + ); + break; + case 'not': + knexRef[`${key}NotBetween`]( + columnAliases[matches[2]] || matches[2], + range + ); + break; + case '': + knexRef[`${key}Between`]( + columnAliases[matches[2]] || matches[2], + range + ); + break; + default: + throw new Error(`${matches[1]} : Invalid operation.`); + break; + } } - } break; - case 'nbtw': { - const range = matches[4].split(','); - if (range.length !== 2) throw new Error(`${matches[4]} : not a valid value.${range.length > 2 ? ' Between accepts only 2 values' : ' Between requires 2 values'}`); - switch ((matches[1] || '')) { - case 'or': - knexRef[`or${camKey}`](builder => builder[`${key}NotBetween`](columnAliases[matches[2]] || matches[2], range)); - break; - case 'and': - knexRef[`${key}NotBetween`](columnAliases[matches[2]] || matches[2], range) - break; - case 'andnot': - knexRef[`${key}Between`](columnAliases[matches[2]] || matches[2], range) - break; - case 'ornot': - knexRef[`or${camKey}`](builder => builder[`${key}Between`](columnAliases[matches[2]] || matches[2], range)); - break; - case 'not': - knexRef[`${key}Between`](columnAliases[matches[2]] || matches[2], range) - break; - case '': - knexRef[`${key}NotBetween`](columnAliases[matches[2]] || matches[2], range) - break; - default: - throw new Error(`${columnAliases[matches[2]] || matches[2]} : Invalid operation.`) - break; + case 'nbtw': + { + const range = matches[4].split(','); + if (range.length !== 2) + throw new Error( + `${matches[4]} : not a valid value.${ + range.length > 2 + ? ' Between accepts only 2 values' + : ' Between requires 2 values' + }` + ); + switch (matches[1] || '') { + case 'or': + knexRef[`or${camKey}`](builder => + builder[`${key}NotBetween`]( + columnAliases[matches[2]] || matches[2], + range + ) + ); + break; + case 'and': + knexRef[`${key}NotBetween`]( + columnAliases[matches[2]] || matches[2], + range + ); + break; + case 'andnot': + knexRef[`${key}Between`]( + columnAliases[matches[2]] || matches[2], + range + ); + break; + case 'ornot': + knexRef[`or${camKey}`](builder => + builder[`${key}Between`]( + columnAliases[matches[2]] || matches[2], + range + ) + ); + break; + case 'not': + knexRef[`${key}Between`]( + columnAliases[matches[2]] || matches[2], + range + ); + break; + case '': + knexRef[`${key}NotBetween`]( + columnAliases[matches[2]] || matches[2], + range + ); + break; + default: + throw new Error( + `${columnAliases[matches[2]] || + matches[2]} : Invalid operation.` + ); + break; + } } - } break; default: - - if (!(matches[3] in opMapping)) throw new Error(`${matches[3]} : Invalid comparison operator`) - switch ((matches[1] || '')) { + if (!(matches[3] in opMapping)) + throw new Error(`${matches[3]} : Invalid comparison operator`); + switch (matches[1] || '') { case 'or': - knexRef[`or${camKey}`](columnAliases[matches[2]] || matches[2], opMapping[matches[3]], matches[4]); + knexRef[`or${camKey}`]( + columnAliases[matches[2]] || matches[2], + opMapping[matches[3]], + matches[4] + ); break; case 'and': - knexRef[`and${camKey}`](columnAliases[matches[2]] || matches[2], opMapping[matches[3]], matches[4]) + knexRef[`and${camKey}`]( + columnAliases[matches[2]] || matches[2], + opMapping[matches[3]], + matches[4] + ); break; case 'andnot': - knexRef[`and${camKey}Not`](columnAliases[matches[2]] || matches[2], opMapping[matches[3]], matches[4]) + knexRef[`and${camKey}Not`]( + columnAliases[matches[2]] || matches[2], + opMapping[matches[3]], + matches[4] + ); break; case 'ornot': - knexRef[`or${camKey}Not`](columnAliases[matches[2]] || matches[2], opMapping[matches[3]], matches[4]) + knexRef[`or${camKey}Not`]( + columnAliases[matches[2]] || matches[2], + opMapping[matches[3]], + matches[4] + ); break; case 'not': - knexRef[`${key}Not`](columnAliases[matches[2]] || matches[2], opMapping[matches[3]], matches[4]) + knexRef[`${key}Not`]( + columnAliases[matches[2]] || matches[2], + opMapping[matches[3]], + matches[4] + ); break; case '': - knexRef[`${key}`](columnAliases[matches[2]] || matches[2], opMapping[matches[3]], matches[4]) + knexRef[`${key}`]( + columnAliases[matches[2]] || matches[2], + opMapping[matches[3]], + matches[4] + ); break; default: - throw new Error(`${matches[1] || ''} Invalid operation.`) + throw new Error(`${matches[1] || ''} Invalid operation.`); break; } break; @@ -277,54 +429,68 @@ const appendWhereCondition = function (conditions, columnAliases: { } else { throw new Error('appendWhereCondition : grammar error ' + conditions); } - }) + }); return knexRef; -} +}; declare module 'knex' { - interface QueryInterface { clientType(): string; - } export type XcConditionObjVal = { - [key in 'eq' | 'neq' | 'lt' | 'gt' | 'ge' | 'le' | 'like' | 'nlike']: string | number | any - } + [key in 'eq' | 'neq' | 'lt' | 'gt' | 'ge' | 'le' | 'like' | 'nlike']: + | string + | number + | any; + }; export interface XcXonditionObj { + _or: XcXonditionObj[]; + _and: XcXonditionObj[]; + _not: XcXonditionObj; - _or: XcXonditionObj[], - _and: XcXonditionObj[], - _not: XcXonditionObj - - [key: string]: XcXonditionObj | XcXonditionObj[], - + [key: string]: XcXonditionObj | XcXonditionObj[]; } interface QueryBuilder { - xwhere(value: string, columnAliases?: { - [columnAlias: string]: string - }): Knex.QueryBuilder; - - condition(conditionObj: XcXonditionObj, columnAliases?: { - [columnAlias: string]: string - }): Knex.QueryBuilder; + xwhere( + value: string, + columnAliases?: { + [columnAlias: string]: string; + } + ): Knex.QueryBuilder; - conditionGraph(condition: { condition: XcXonditionObj, models: { [key: string]: BaseModelSql } }): Knex.QueryBuilder; + condition( + conditionObj: XcXonditionObj, + columnAliases?: { + [columnAlias: string]: string; + } + ): Knex.QueryBuilder; - xhaving(value: string, columnAliases?: { - [columnAlias: string]: string + conditionGraph(condition: { + condition: XcXonditionObj; + models: { [key: string]: BaseModelSql }; }): Knex.QueryBuilder; + + xhaving( + value: string, + columnAliases?: { + [columnAlias: string]: string; + } + ): Knex.QueryBuilder; } } /** * Append xwhere to knex query builder */ -Knex.QueryBuilder.extend('xwhere', function (conditionString, columnAliases?: { - [columnAlias: string]: string|any -}) { +Knex.QueryBuilder.extend('xwhere', function( + conditionString, + columnAliases?: { + [columnAlias: string]: string | any; + } +) { const conditions = toArrayOfConditions(conditionString); return appendWhereCondition(conditions, columnAliases || {}, this); }); @@ -332,9 +498,12 @@ Knex.QueryBuilder.extend('xwhere', function (conditionString, columnAliases?: { /** * Append xhaving to knex query builder */ -Knex.QueryBuilder.extend('xhaving', function (conditionString, columnAliases?: { - [columnAlias: string]: string -}) { +Knex.QueryBuilder.extend('xhaving', function( + conditionString, + columnAliases?: { + [columnAlias: string]: string; + } +) { const conditions = toArrayOfConditions(conditionString); return appendWhereCondition(conditions, columnAliases || {}, this, true); }); @@ -342,42 +511,40 @@ Knex.QueryBuilder.extend('xhaving', function (conditionString, columnAliases?: { /** * Append custom where condition(nested object) to knex query builder */ -Knex.QueryBuilder.extend('condition', function (conditionObj, columnAliases) { +Knex.QueryBuilder.extend('condition', function(conditionObj, columnAliases) { if (!conditionObj || typeof conditionObj !== 'object') { return this; } return parseCondition(conditionObj, columnAliases || {}, this); }); - const parseCondition = (obj, columnAliases, qb, pKey?) => { const conditions = Object.entries(obj); - for (const [key, val] of conditions) { switch (key) { case '_or': - qb = qb.where(function () { + qb = qb.where(function() { for (const condition of val as any[]) { - this.orWhere(function () { + this.orWhere(function() { return parseCondition(condition, columnAliases, this); - }) + }); } }); break; case '_and': - qb = qb.where(function () { + qb = qb.where(function() { for (const condition of val as any[]) { - this.andWhere(function () { + this.andWhere(function() { return parseCondition(condition, columnAliases, this); - }) + }); } }); break; case '_not': - qb = qb.whereNot(function () { + qb = qb.whereNot(function() { return parseCondition(val, columnAliases, this); - }) + }); break; default: if (typeof val === 'object' && !Array.isArray(val)) { @@ -419,20 +586,20 @@ const parseCondition = (obj, columnAliases, qb, pKey?) => { } break; } - } return qb; - - -} +}; // todo: optimize -Knex.QueryBuilder.extend('conditionGraph', function (args: { condition, models }) { +Knex.QueryBuilder.extend('conditionGraph', function(args: { + condition; + models; +}) { if (!args) { return this; } - const {condition, models} = args; + const { condition, models } = args; if (!condition || typeof condition !== 'object') { return this; } @@ -440,12 +607,11 @@ Knex.QueryBuilder.extend('conditionGraph', function (args: { condition, models } const conditionCopy = JSON.parse(JSON.stringify(condition)); // parse and do all the joins - const qb = parseNestedConditionAndJoin.call({models}, conditionCopy, this); + const qb = parseNestedConditionAndJoin.call({ models }, conditionCopy, this); // parse and define all where conditions - return parseNestedCondition.call({models}, conditionCopy, qb); + return parseNestedCondition.call({ models }, conditionCopy, qb); }); - // @ts-ignore function parseNestedConditionAndJoin(obj, qb, pKey?, table?, tableAlias?) { this.alias = this.alias || {}; @@ -456,60 +622,80 @@ function parseNestedConditionAndJoin(obj, qb, pKey?, table?, tableAlias?) { // check for relation if (typeof obj === 'object' && 'relationType' in obj) { - switch (obj.relationType) { - case 'hm': { - // const model = Object.entries(models).find(([name]) => { - // // todo: name comparison - // return pKey.toLowerCase().startsWith(name.toLowerCase()); - // })?.[1]; - - // todo: get tablename from model - const relation = this?.models?.[table || qb._single.table]?.hasManyRelations?.find(({tn}) => pKey.toLowerCase() === tn.toLowerCase()) - - // if (model) { - // console.log(model) - // } - if (relation) { - this.alias[relation.tn] = (this.alias[relation.tn] || 0) + 1; - - obj.relationType = { - alias: `${this.alias[relation.tn] ? this.alias[relation.tn] + '___' : ''}${relation.tn}`, - type: obj.relationType - }; - - - qb = qb.join(`${relation.tn} as ${obj.relationType.alias}`, `${obj.relationType.alias}.${relation.cn}`, '=', `${tableAlias}.${relation.rcn}`) - // delete obj.relationType; - // return parseNestedConditionAndJoin.call(this, Object.entries(obj).find(([k]) => k !== 'relationType')?.[1], qb, Object.keys(obj).find(k => k !== 'relationType'), relation.tn) - tn = relation.tn; - conditions = conditions.filter(c => c[0] !== 'relationType') - - tableAlias = obj.relationType.alias; + case 'hm': + { + // const model = Object.entries(models).find(([name]) => { + // // todo: name comparison + // return pKey.toLowerCase().startsWith(name.toLowerCase()); + // })?.[1]; + + // todo: get tablename from model + const relation = this?.models?.[ + table || qb._single.table + ]?.hasManyRelations?.find( + ({ tn }) => pKey.toLowerCase() === tn.toLowerCase() + ); + + // if (model) { + // console.log(model) + // } + if (relation) { + this.alias[relation.tn] = (this.alias[relation.tn] || 0) + 1; + + obj.relationType = { + alias: `${ + this.alias[relation.tn] ? this.alias[relation.tn] + '___' : '' + }${relation.tn}`, + type: obj.relationType + }; + + qb = qb.join( + `${relation.tn} as ${obj.relationType.alias}`, + `${obj.relationType.alias}.${relation.cn}`, + '=', + `${tableAlias}.${relation.rcn}` + ); + // delete obj.relationType; + // return parseNestedConditionAndJoin.call(this, Object.entries(obj).find(([k]) => k !== 'relationType')?.[1], qb, Object.keys(obj).find(k => k !== 'relationType'), relation.tn) + tn = relation.tn; + conditions = conditions.filter(c => c[0] !== 'relationType'); + + tableAlias = obj.relationType.alias; + } } - } break; - case 'bt': { - // todo: get tablename from model - const relation = this?.models?.[table || qb._single.table]?.belongsToRelations?.find(({rtn}) => pKey.toLowerCase() === rtn.toLowerCase()) - - // if (model) { - // console.log(model) - // } - if (relation) { - this.alias[relation.rtn] = (this.alias[relation.rtn] || 0) + 1; - obj.relationType = { - alias: `${this.alias[relation.rtn]}___${relation.rtn}`, - type: obj.relationType - }; - qb = qb.join(`${relation.rtn} as ${obj.relationType.alias}`, `${tableAlias}.${relation.cn}`, '=', `${obj.relationType.alias}.${relation.rcn}`) - // delete obj.relationType; - // return parseNestedConditionAndJoin.call(self, Object.entries(obj).find(([k]) => k !== 'relationType')?.[1], qb, Object.keys(obj).find(k => k !== 'relationType'), relation.rtn) - tn = relation.rtn; - conditions = conditions.filter(c => c[0] !== 'relationType'); - tableAlias = obj.relationType.alias; + case 'bt': + { + // todo: get tablename from model + const relation = this?.models?.[ + table || qb._single.table + ]?.belongsToRelations?.find( + ({ rtn }) => pKey.toLowerCase() === rtn.toLowerCase() + ); + + // if (model) { + // console.log(model) + // } + if (relation) { + this.alias[relation.rtn] = (this.alias[relation.rtn] || 0) + 1; + obj.relationType = { + alias: `${this.alias[relation.rtn]}___${relation.rtn}`, + type: obj.relationType + }; + qb = qb.join( + `${relation.rtn} as ${obj.relationType.alias}`, + `${tableAlias}.${relation.cn}`, + '=', + `${obj.relationType.alias}.${relation.rcn}` + ); + // delete obj.relationType; + // return parseNestedConditionAndJoin.call(self, Object.entries(obj).find(([k]) => k !== 'relationType')?.[1], qb, Object.keys(obj).find(k => k !== 'relationType'), relation.rtn) + tn = relation.rtn; + conditions = conditions.filter(c => c[0] !== 'relationType'); + tableAlias = obj.relationType.alias; + } } - } break; default: break; @@ -522,15 +708,36 @@ function parseNestedConditionAndJoin(obj, qb, pKey?, table?, tableAlias?) { case '_or': case '_and': for (const condition of val as any[]) { - qb = parseNestedConditionAndJoin.call(self, condition, qb, null, tn, tableAlias); + qb = parseNestedConditionAndJoin.call( + self, + condition, + qb, + null, + tn, + tableAlias + ); } break; case '_not': - qb = parseNestedConditionAndJoin.call(self, val, qb, null, tn, tableAlias); + qb = parseNestedConditionAndJoin.call( + self, + val, + qb, + null, + tn, + tableAlias + ); break; default: if (typeof val === 'object' && !Array.isArray(val)) { - qb = parseNestedConditionAndJoin.call(self, val, qb, key, tn, tableAlias); + qb = parseNestedConditionAndJoin.call( + self, + val, + qb, + key, + tn, + tableAlias + ); } } } @@ -549,46 +756,55 @@ function parseNestedCondition(obj, qb, pKey?, table?, tableAlias?) { if ('relationType' in obj) { // alias = {...self.alias}; switch (obj.relationType.type) { - case 'hm': { - - // const model = Object.entries(models).find(([name]) => { - // // todo: name comparison - // return pKey.toLowerCase().startsWith(name.toLowerCase()); - // })?.[1]; - - // todo: get tablename from model - const relation = this?.models?.[table || qb._single.table]?.hasManyRelations?.find(({tn}) => pKey.toLowerCase() === tn.toLowerCase()) - - // if (model) { - // console.log(model) - // } - - if (relation) { - // alias[relation.tn] = this.globalAlias[relation.tn] = (this.globalAlias[relation.tn] || 0) + 1; - // qb = qb.join(relation.tn, `${relation.tn}.${relation.cn}`, '=', `${relation.rtn}.${relation.rcn}`) - // delete obj.relationType; - // return parseNestedCondition.call(this, Object.values(obj)[0], qb, Object.keys(obj)[0], - tn = relation.tn; - tableAlias = obj.relationType.alias; + case 'hm': + { + // const model = Object.entries(models).find(([name]) => { + // // todo: name comparison + // return pKey.toLowerCase().startsWith(name.toLowerCase()); + // })?.[1]; + + // todo: get tablename from model + const relation = this?.models?.[ + table || qb._single.table + ]?.hasManyRelations?.find( + ({ tn }) => pKey.toLowerCase() === tn.toLowerCase() + ); + + // if (model) { + // console.log(model) + // } + + if (relation) { + // alias[relation.tn] = this.globalAlias[relation.tn] = (this.globalAlias[relation.tn] || 0) + 1; + // qb = qb.join(relation.tn, `${relation.tn}.${relation.cn}`, '=', `${relation.rtn}.${relation.rcn}`) + // delete obj.relationType; + // return parseNestedCondition.call(this, Object.values(obj)[0], qb, Object.keys(obj)[0], + tn = relation.tn; + tableAlias = obj.relationType.alias; + } } - } break; - case 'bt': { - // todo: get tablename from model - const relation = this?.models?.[table || qb._single.table]?.belongsToRelations?.find(({rtn}) => pKey.toLowerCase() === rtn.toLowerCase()) - - // if (model) { - // console.log(model) - // } - if (relation) { - // alias[relation.rtn] = this.globalAlias[relation.rtn] = (this.globalAlias[relation.rtn] || 0) + 1; - // qb = qb.join(relation.rtn, `${relation.tn}.${relation.cn}`, '=', `${relation.rtn}.${relation.rcn}`) - // delete obj.relationType; - // return parseNestedCondition.call(self, Object.values(obj)[0], qb, Object.keys(obj)[0], - tn = relation.rtn; - tableAlias = obj.relationType.alias; + case 'bt': + { + // todo: get tablename from model + const relation = this?.models?.[ + table || qb._single.table + ]?.belongsToRelations?.find( + ({ rtn }) => pKey.toLowerCase() === rtn.toLowerCase() + ); + + // if (model) { + // console.log(model) + // } + if (relation) { + // alias[relation.rtn] = this.globalAlias[relation.rtn] = (this.globalAlias[relation.rtn] || 0) + 1; + // qb = qb.join(relation.rtn, `${relation.tn}.${relation.cn}`, '=', `${relation.rtn}.${relation.rcn}`) + // delete obj.relationType; + // return parseNestedCondition.call(self, Object.values(obj)[0], qb, Object.keys(obj)[0], + tn = relation.rtn; + tableAlias = obj.relationType.alias; + } } - } break; default: break; @@ -605,28 +821,48 @@ function parseNestedCondition(obj, qb, pKey?, table?, tableAlias?) { // handle logical operators recursively switch (key) { case '_or': - qb = qb.where(function () { + qb = qb.where(function() { for (const condition of val as any[]) { - this.orWhere(function () { - return parseNestedCondition.call(self, condition, this, null, tn, tableAlias); - }) + this.orWhere(function() { + return parseNestedCondition.call( + self, + condition, + this, + null, + tn, + tableAlias + ); + }); } }); break; case '_and': - - qb = qb.where(function () { + qb = qb.where(function() { for (const condition of val as any[]) { - this.andWhere(function () { - parseNestedCondition.call(self, condition, this, null, tn, tableAlias); - }) + this.andWhere(function() { + parseNestedCondition.call( + self, + condition, + this, + null, + tn, + tableAlias + ); + }); } }); break; case '_not': - qb = qb.whereNot(function () { - return parseNestedCondition.call(self, val, this, null, tn, tableAlias); - }) + qb = qb.whereNot(function() { + return parseNestedCondition.call( + self, + val, + this, + null, + tn, + tableAlias + ); + }); break; default: // if object handle recursively @@ -669,17 +905,14 @@ function parseNestedCondition(obj, qb, pKey?, table?, tableAlias?) { } break; } - } return qb; - } type CustomKnex = Knex; -function CustomKnex(arg: string | Knex.Config|any): CustomKnex { - +function CustomKnex(arg: string | Knex.Config | any): CustomKnex { const knex: any = Knex(arg); const knexRaw = knex.raw; @@ -700,16 +933,19 @@ function CustomKnex(arg: string | Knex.Config|any): CustomKnex { value: (...args) => { return knexRaw.apply(knex, args); } - }, clientType: { + }, + clientType: { enumerable: true, value: () => { - return typeof arg === 'string' ? arg.match(/^(\w+):/) ?? [1] : arg.client; + return typeof arg === 'string' + ? arg.match(/^(\w+):/) ?? [1] + : arg.client; } }, searchPath: { enumerable: true, value: () => { - return arg?.searchPath?.[0] + return arg?.searchPath?.[0]; } } }); @@ -726,9 +962,9 @@ function CustomKnex(arg: string | Knex.Config|any): CustomKnex { return knex; } - export default CustomKnex; -export {Knex}/** +export { Knex }; +/** * @copyright Copyright (c) 2021, Xgene Cloud Ltd * * @author Naveen MR diff --git a/packages/nocodb/src/lib/dataMapper/lib/sql/formulaQueryBuilderFromString.ts b/packages/nocodb/src/lib/dataMapper/lib/sql/formulaQueryBuilderFromString.ts index 2fc41c44fb..8e23e43930 100644 --- a/packages/nocodb/src/lib/dataMapper/lib/sql/formulaQueryBuilderFromString.ts +++ b/packages/nocodb/src/lib/dataMapper/lib/sql/formulaQueryBuilderFromString.ts @@ -1,30 +1,37 @@ import jsep from 'jsep'; -import mapFunctionName from "./mapFunctionName"; - +import mapFunctionName from './mapFunctionName'; // todo: switch function based on database export function formulaQueryBuilderFromString(str, alias, knex) { - return formulaQueryBuilder(jsep(str), alias, knex) + return formulaQueryBuilder(jsep(str), alias, knex); } - -export default function formulaQueryBuilder(tree, alias, knex, aliasToColumn = {}) { - const fn = (pt, a?, prevBinaryOp ?) => { +export default function formulaQueryBuilder( + tree, + alias, + knex, + aliasToColumn = {} +) { + const fn = (pt, a?, prevBinaryOp?) => { const colAlias = a ? ` as ${a}` : ''; if (pt.type === 'CallExpression') { switch (pt.callee.name) { case 'ADD': case 'SUM': if (pt.arguments.length > 1) { - return fn({ - type: 'BinaryExpression', - operator: '+', - left: pt.arguments[0], - right: {...pt, arguments: pt.arguments.slice(1)} - }, a, prevBinaryOp) + return fn( + { + type: 'BinaryExpression', + operator: '+', + left: pt.arguments[0], + right: { ...pt, arguments: pt.arguments.slice(1) } + }, + a, + prevBinaryOp + ); } else { - return fn(pt.arguments[0], a, prevBinaryOp) + return fn(pt.arguments[0], a, prevBinaryOp); } break; // case 'AVG': @@ -42,54 +49,72 @@ export default function formulaQueryBuilder(tree, alias, knex, aliasToColumn = { case 'CONCAT': if (knex.clientType() === 'sqlite3') { if (pt.arguments.length > 1) { - return fn({ - type: 'BinaryExpression', - operator: '||', - left: pt.arguments[0], - right: {...pt, arguments: pt.arguments.slice(1)} - }, a, prevBinaryOp) + return fn( + { + type: 'BinaryExpression', + operator: '||', + left: pt.arguments[0], + right: { ...pt, arguments: pt.arguments.slice(1) } + }, + a, + prevBinaryOp + ); } else { - return fn(pt.arguments[0], a, prevBinaryOp) + return fn(pt.arguments[0], a, prevBinaryOp); } } break; - default: { - const res = mapFunctionName({pt, knex, alias, a, aliasToCol: aliasToColumn, fn, colAlias, prevBinaryOp}) - if (res) return res; - } - break + default: + { + const res = mapFunctionName({ + pt, + knex, + alias, + a, + aliasToCol: aliasToColumn, + fn, + colAlias, + prevBinaryOp + }); + if (res) return res; + } + break; } - return knex.raw(`${pt.callee.name}(${pt.arguments.map(arg => fn(arg).toQuery()).join()})${colAlias}`) + return knex.raw( + `${pt.callee.name}(${pt.arguments + .map(arg => fn(arg).toQuery()) + .join()})${colAlias}` + ); } else if (pt.type === 'Literal') { return knex.raw(`?${colAlias}`, [pt.value]); } else if (pt.type === 'Identifier') { return knex.raw(`??${colAlias}`, [aliasToColumn[pt.name] || pt.name]); } else if (pt.type === 'BinaryExpression') { if (pt.operator === '==') { - pt.operator = '=' + pt.operator = '='; } if (pt.operator === '/') { pt.left = { - callee: {name: 'FLOAT'}, + callee: { name: 'FLOAT' }, type: 'CallExpression', - arguments: [ - pt.left - ] - } + arguments: [pt.left] + }; } - - const query = knex.raw(`${fn(pt.left, null, pt.operator).toQuery()} ${pt.operator} ${fn(pt.right, null, pt.operator).toQuery()}${colAlias}`) + const query = knex.raw( + `${fn(pt.left, null, pt.operator).toQuery()} ${pt.operator} ${fn( + pt.right, + null, + pt.operator + ).toQuery()}${colAlias}` + ); if (prevBinaryOp && pt.operator !== prevBinaryOp) { - query.wrap('(', ')') + query.wrap('(', ')'); } return query; } }; - return fn(tree, alias) + return fn(tree, alias); } - - - diff --git a/packages/nocodb/src/lib/dataMapper/lib/sql/functionMappings/commonFns.ts b/packages/nocodb/src/lib/dataMapper/lib/sql/functionMappings/commonFns.ts index 9ac9c168be..a670a0e193 100644 --- a/packages/nocodb/src/lib/dataMapper/lib/sql/functionMappings/commonFns.ts +++ b/packages/nocodb/src/lib/dataMapper/lib/sql/functionMappings/commonFns.ts @@ -1,49 +1,87 @@ -import {MapFnArgs} from "../mapFunctionName"; +import { MapFnArgs } from '../mapFunctionName'; export default { // todo: handle default case SWITCH: (args: MapFnArgs) => { - const count = Math.floor((args.pt.arguments.length - 1) / 2) + const count = Math.floor((args.pt.arguments.length - 1) / 2); let query = ''; const switchVal = args.fn(args.pt.arguments[0]).toQuery(); for (let i = 0; i < count; i++) { - query += args.knex.raw(`\n\tWHEN ${args.fn(args.pt.arguments[i * 2 + 1]).toQuery()} THEN ${args.fn(args.pt.arguments[i * 2 + 2]).toQuery()}`).toQuery() + query += args.knex + .raw( + `\n\tWHEN ${args + .fn(args.pt.arguments[i * 2 + 1]) + .toQuery()} THEN ${args.fn(args.pt.arguments[i * 2 + 2]).toQuery()}` + ) + .toQuery(); } if (args.pt.arguments.length % 2 === 0) { - query += args.knex.raw(`\n\tELSE ${args.fn(args.pt.arguments[args.pt.arguments.length - 1]).toQuery()}`).toQuery() + query += args.knex + .raw( + `\n\tELSE ${args + .fn(args.pt.arguments[args.pt.arguments.length - 1]) + .toQuery()}` + ) + .toQuery(); } - return args.knex.raw(`CASE ${switchVal} ${query}\n END${args.colAlias}`) + return args.knex.raw(`CASE ${switchVal} ${query}\n END${args.colAlias}`); }, IF: (args: MapFnArgs) => { - let query = args.knex.raw(`\n\tWHEN ${args.fn(args.pt.arguments[0]).toQuery()} THEN ${args.fn(args.pt.arguments[1]).toQuery()}`).toQuery(); + let query = args.knex + .raw( + `\n\tWHEN ${args.fn(args.pt.arguments[0]).toQuery()} THEN ${args + .fn(args.pt.arguments[1]) + .toQuery()}` + ) + .toQuery(); if (args.pt.arguments[2]) { - query += args.knex.raw(`\n\tELSE ${args.fn(args.pt.arguments[2]).toQuery()}`).toQuery() + query += args.knex + .raw(`\n\tELSE ${args.fn(args.pt.arguments[2]).toQuery()}`) + .toQuery(); } - return args.knex.raw(`CASE ${query}\n END${args.colAlias}`) + return args.knex.raw(`CASE ${query}\n END${args.colAlias}`); }, - TRUE: (_args) => 1, - FALSE: (_args) => 0, + TRUE: _args => 1, + FALSE: _args => 0, AND: (args: MapFnArgs) => { - return args.knex.raw(`${args.knex.raw(`${args.pt.arguments.map(ar => args.fn(ar).toQuery()).join(' AND ')}`).wrap('(', ')').toQuery()}${args.colAlias}`) + return args.knex.raw( + `${args.knex + .raw( + `${args.pt.arguments.map(ar => args.fn(ar).toQuery()).join(' AND ')}` + ) + .wrap('(', ')') + .toQuery()}${args.colAlias}` + ); }, OR: (args: MapFnArgs) => { - return args.knex.raw(`${args.knex.raw(`${args.pt.arguments.map(ar => args.fn(ar).toQuery()).join(' OR ')}`).wrap('(', ')').toQuery()}${args.colAlias}`) + return args.knex.raw( + `${args.knex + .raw( + `${args.pt.arguments.map(ar => args.fn(ar).toQuery()).join(' OR ')}` + ) + .wrap('(', ')') + .toQuery()}${args.colAlias}` + ); }, AVG: (args: MapFnArgs) => { if (args.pt.arguments.length > 1) { - return args.fn({ - type: 'BinaryExpression', - operator: '/', - left: {...args.pt, callee: {name: 'SUM'}}, - right: {type: 'Literal', value: args.pt.arguments.length} - }, args.a, args.prevBinaryOp) + return args.fn( + { + type: 'BinaryExpression', + operator: '/', + left: { ...args.pt, callee: { name: 'SUM' } }, + right: { type: 'Literal', value: args.pt.arguments.length } + }, + args.a, + args.prevBinaryOp + ); } else { - return args.fn(args.pt.arguments[0], args.a, args.prevBinaryOp) + return args.fn(args.pt.arguments[0], args.a, args.prevBinaryOp); } }, FLOAT: (args: MapFnArgs) => { - return args.fn(args.pt?.arguments?.[0]).wrap('(',')'); + return args.fn(args.pt?.arguments?.[0]).wrap('(', ')'); } -} +}; diff --git a/packages/nocodb/src/lib/dataMapper/lib/sql/functionMappings/mssql.ts b/packages/nocodb/src/lib/dataMapper/lib/sql/functionMappings/mssql.ts index 4e1479d613..223bc517f4 100644 --- a/packages/nocodb/src/lib/dataMapper/lib/sql/functionMappings/mssql.ts +++ b/packages/nocodb/src/lib/dataMapper/lib/sql/functionMappings/mssql.ts @@ -1,59 +1,86 @@ -import {MapFnArgs} from "../mapFunctionName"; -import commonFns from "./commonFns"; +import { MapFnArgs } from '../mapFunctionName'; +import commonFns from './commonFns'; const mssql = { ...commonFns, MIN: (args: MapFnArgs) => { if (args.pt.arguments.length === 1) { - return args.fn(args.pt.arguments[0]) + return args.fn(args.pt.arguments[0]); } let query = ''; for (const [i, arg] of Object.entries(args.pt.arguments)) { if (+i === args.pt.arguments.length - 1) { - query += args.knex.raw(`\n\tElse ${args.fn(arg).toQuery()}`).toQuery() + query += args.knex.raw(`\n\tElse ${args.fn(arg).toQuery()}`).toQuery(); } else { - query += args.knex.raw(`\n\tWhen ${args.pt.arguments.filter((_, j) => +i !== j).map(arg1 => `${args.fn(arg).toQuery()} < ${args.fn(arg1).toQuery()}`).join(' And ')} Then ${args.fn(arg).toQuery()}`).toQuery() + query += args.knex + .raw( + `\n\tWhen ${args.pt.arguments + .filter((_, j) => +i !== j) + .map( + arg1 => `${args.fn(arg).toQuery()} < ${args.fn(arg1).toQuery()}` + ) + .join(' And ')} Then ${args.fn(arg).toQuery()}` + ) + .toQuery(); } } - return args.knex.raw(`Case ${query}\n End${args.colAlias}`) + return args.knex.raw(`Case ${query}\n End${args.colAlias}`); }, MAX: (args: MapFnArgs) => { if (args.pt.arguments.length === 1) { - return args.fn(args.pt.arguments[0]) + return args.fn(args.pt.arguments[0]); } let query = ''; for (const [i, arg] of Object.entries(args.pt.arguments)) { if (+i === args.pt.arguments.length - 1) { - query += args.knex.raw(`\nElse ${args.fn(arg).toQuery()}`).toQuery() + query += args.knex.raw(`\nElse ${args.fn(arg).toQuery()}`).toQuery(); } else { - query += args.knex.raw(`\nWhen ${args.pt.arguments.filter((_, j) => +i !== j).map(arg1 => `${args.fn(arg).toQuery()} > ${args.fn(arg1).toQuery()}`).join(' And ')} Then ${args.fn(arg).toQuery()}`).toQuery() + query += args.knex + .raw( + `\nWhen ${args.pt.arguments + .filter((_, j) => +i !== j) + .map( + arg1 => `${args.fn(arg).toQuery()} > ${args.fn(arg1).toQuery()}` + ) + .join(' And ')} Then ${args.fn(arg).toQuery()}` + ) + .toQuery(); } } - return args.knex.raw(`Case ${query}\n End${args.colAlias}`) + return args.knex.raw(`Case ${query}\n End${args.colAlias}`); }, - MOD: (pt) => { + MOD: pt => { Object.assign(pt, { type: 'BinaryExpression', operator: '%', left: pt.arguments[0], right: pt.arguments[1] - }) + }); }, REPEAT: 'REPLICATE', NOW: 'getdate', SEARCH: (args: MapFnArgs) => { args.pt.callee.name = 'CHARINDEX'; - const temp = args.pt.arguments[0] - args.pt.arguments[0] = args.pt.arguments[1] + const temp = args.pt.arguments[0]; + args.pt.arguments[0] = args.pt.arguments[1]; args.pt.arguments[1] = temp; }, INT: (args: MapFnArgs) => { - return args.knex.raw(`CASE WHEN ISNUMERIC(${args.fn(args.pt.arguments[0]).toQuery()}) = 1 THEN FLOOR(${args.fn(args.pt.arguments[0]).toQuery()}) ELSE 0 END${args.colAlias}`) + return args.knex.raw( + `CASE WHEN ISNUMERIC(${args + .fn(args.pt.arguments[0]) + .toQuery()}) = 1 THEN FLOOR(${args + .fn(args.pt.arguments[0]) + .toQuery()}) ELSE 0 END${args.colAlias}` + ); }, - MID:'SUBSTR', + MID: 'SUBSTR', FLOAT: (args: MapFnArgs) => { - return args.knex.raw(`CAST(${args.fn(args.pt.arguments[0])} as FLOAT)${args.colAlias}`).wrap('(',')') -}} + return args.knex + .raw(`CAST(${args.fn(args.pt.arguments[0])} as FLOAT)${args.colAlias}`) + .wrap('(', ')'); + } +}; export default mssql; diff --git a/packages/nocodb/src/lib/dataMapper/lib/sql/functionMappings/mysql.ts b/packages/nocodb/src/lib/dataMapper/lib/sql/functionMappings/mysql.ts index c58c23a0ff..73e00d3ae3 100644 --- a/packages/nocodb/src/lib/dataMapper/lib/sql/functionMappings/mysql.ts +++ b/packages/nocodb/src/lib/dataMapper/lib/sql/functionMappings/mysql.ts @@ -1,6 +1,5 @@ -import {MapFnArgs} from "../mapFunctionName"; -import commonFns from "./commonFns"; - +import { MapFnArgs } from '../mapFunctionName'; +import commonFns from './commonFns'; const mysql2 = { ...commonFns, @@ -9,25 +8,35 @@ const mysql2 = { MAX: 'GREATEST', SEARCH: (args: MapFnArgs) => { args.pt.callee.name = 'LOCATE'; - const temp = args.pt.arguments[0] - args.pt.arguments[0] = args.pt.arguments[1] + const temp = args.pt.arguments[0]; + args.pt.arguments[0] = args.pt.arguments[1]; args.pt.arguments[1] = temp; }, - INT:(args: MapFnArgs) =>{ - return args.knex.raw(`CAST(${args.fn(args.pt.arguments[0])} as SIGNED)${args.colAlias}`) + INT: (args: MapFnArgs) => { + return args.knex.raw( + `CAST(${args.fn(args.pt.arguments[0])} as SIGNED)${args.colAlias}` + ); }, - LEFT:(args: MapFnArgs)=> { - return args.knex.raw(`SUBSTR(${args.fn(args.pt.arguments[0])},1,${args.fn(args.pt.arguments[1])})${args.colAlias}`) + LEFT: (args: MapFnArgs) => { + return args.knex.raw( + `SUBSTR(${args.fn(args.pt.arguments[0])},1,${args.fn( + args.pt.arguments[1] + )})${args.colAlias}` + ); }, - RIGHT:(args: MapFnArgs)=> { - return args.knex.raw(`SUBSTR(${args.fn(args.pt.arguments[0])},-${args.fn(args.pt.arguments[1])})${args.colAlias}`) + RIGHT: (args: MapFnArgs) => { + return args.knex.raw( + `SUBSTR(${args.fn(args.pt.arguments[0])},-${args.fn( + args.pt.arguments[1] + )})${args.colAlias}` + ); }, - MID:'SUBSTR', + MID: 'SUBSTR', FLOAT: (args: MapFnArgs) => { - return args.knex.raw(`CAST(${args.fn(args.pt.arguments[0])} as DOUBLE)${args.colAlias}`).wrap('(',')') + return args.knex + .raw(`CAST(${args.fn(args.pt.arguments[0])} as DOUBLE)${args.colAlias}`) + .wrap('(', ')'); } -} - +}; export default mysql2; - diff --git a/packages/nocodb/src/lib/dataMapper/lib/sql/functionMappings/pg.ts b/packages/nocodb/src/lib/dataMapper/lib/sql/functionMappings/pg.ts index fe74d094db..41519fa11c 100644 --- a/packages/nocodb/src/lib/dataMapper/lib/sql/functionMappings/pg.ts +++ b/packages/nocodb/src/lib/dataMapper/lib/sql/functionMappings/pg.ts @@ -1,5 +1,5 @@ -import {MapFnArgs} from "../mapFunctionName"; -import commonFns from "./commonFns"; +import { MapFnArgs } from '../mapFunctionName'; +import commonFns from './commonFns'; const pg = { ...commonFns, @@ -11,17 +11,32 @@ const pg = { POWER: 'pow', SQRT: 'sqrt', SEARCH: (args: MapFnArgs) => { - return args.knex.raw(`POSITION(${args.knex.raw(args.fn(args.pt.arguments[1]).toQuery())} in ${args.knex.raw(args.fn(args.pt.arguments[0]).toQuery())})${args.colAlias}`) + return args.knex.raw( + `POSITION(${args.knex.raw( + args.fn(args.pt.arguments[1]).toQuery() + )} in ${args.knex.raw(args.fn(args.pt.arguments[0]).toQuery())})${ + args.colAlias + }` + ); }, INT(args: MapFnArgs) { // todo: correction - return args.knex.raw(`REGEXP_REPLACE(COALESCE(${args.fn(args.pt.arguments[0])}::character varying, '0'), '[^0-9]+|\\.[0-9]+' ,'')${args.colAlias}`) + return args.knex.raw( + `REGEXP_REPLACE(COALESCE(${args.fn( + args.pt.arguments[0] + )}::character varying, '0'), '[^0-9]+|\\.[0-9]+' ,'')${args.colAlias}` + ); }, MID: 'SUBSTR', FLOAT: (args: MapFnArgs) => { - return args.knex.raw(`CAST(${args.fn(args.pt.arguments[0])} as DOUBLE PRECISION)${args.colAlias}`).wrap('(',')') + return args.knex + .raw( + `CAST(${args.fn(args.pt.arguments[0])} as DOUBLE PRECISION)${ + args.colAlias + }` + ) + .wrap('(', ')'); } -} - +}; export default pg; diff --git a/packages/nocodb/src/lib/dataMapper/lib/sql/functionMappings/sqlite.ts b/packages/nocodb/src/lib/dataMapper/lib/sql/functionMappings/sqlite.ts index a48c272c4d..d8ca5564de 100644 --- a/packages/nocodb/src/lib/dataMapper/lib/sql/functionMappings/sqlite.ts +++ b/packages/nocodb/src/lib/dataMapper/lib/sql/functionMappings/sqlite.ts @@ -1,14 +1,18 @@ -import {MapFnArgs} from "../mapFunctionName"; -import commonFns from "./commonFns"; - +import { MapFnArgs } from '../mapFunctionName'; +import commonFns from './commonFns'; const sqlite3 = { ...commonFns, LEN: 'LENGTH', CEILING(args) { - return args.knex.raw(`round(${args.fn(args.pt.arguments[0])} + 0.5)${args.colAlias}`) - }, FLOOR(args) { - return args.knex.raw(`round(${args.fn(args.pt.arguments[0])} - 0.5)${args.colAlias}`) + return args.knex.raw( + `round(${args.fn(args.pt.arguments[0])} + 0.5)${args.colAlias}` + ); + }, + FLOOR(args) { + return args.knex.raw( + `round(${args.fn(args.pt.arguments[0])} - 0.5)${args.colAlias}` + ); }, MOD: (args: MapFnArgs) => { return args.fn({ @@ -16,27 +20,42 @@ const sqlite3 = { operator: '%', left: args.pt.arguments[0], right: args.pt.arguments[1] - }) + }); }, REPEAT(args: MapFnArgs) { - return args.knex.raw(`replace(printf('%.' || ${args.fn(args.pt.arguments[1])} || 'c', '/'),'/',${args.fn(args.pt.arguments[0])})${args.colAlias}`) + return args.knex.raw( + `replace(printf('%.' || ${args.fn( + args.pt.arguments[1] + )} || 'c', '/'),'/',${args.fn(args.pt.arguments[0])})${args.colAlias}` + ); }, NOW: 'DATE', SEARCH: 'INSTR', INT(args: MapFnArgs) { - return args.knex.raw(`CAST(${args.fn(args.pt.arguments[0])} as INTEGER)${args.colAlias}`) + return args.knex.raw( + `CAST(${args.fn(args.pt.arguments[0])} as INTEGER)${args.colAlias}` + ); }, LEFT: (args: MapFnArgs) => { - return args.knex.raw(`SUBSTR(${args.fn(args.pt.arguments[0])},1,${args.fn(args.pt.arguments[1])})${args.colAlias}`) + return args.knex.raw( + `SUBSTR(${args.fn(args.pt.arguments[0])},1,${args.fn( + args.pt.arguments[1] + )})${args.colAlias}` + ); }, RIGHT: (args: MapFnArgs) => { - return args.knex.raw(`SUBSTR(${args.fn(args.pt.arguments[0])},-${args.fn(args.pt.arguments[1])})${args.colAlias}`) + return args.knex.raw( + `SUBSTR(${args.fn(args.pt.arguments[0])},-${args.fn( + args.pt.arguments[1] + )})${args.colAlias}` + ); }, MID: 'SUBSTR', FLOAT: (args: MapFnArgs) => { - return args.knex.raw(`CAST(${args.fn(args.pt.arguments[0])} as FLOAT)${args.colAlias}`).wrap('(',')') + return args.knex + .raw(`CAST(${args.fn(args.pt.arguments[0])} as FLOAT)${args.colAlias}`) + .wrap('(', ')'); } -} - +}; export default sqlite3; diff --git a/packages/nocodb/src/lib/dataMapper/lib/sql/genRollupSelect.ts b/packages/nocodb/src/lib/dataMapper/lib/sql/genRollupSelect.ts index 22a4dcf3de..302a66a685 100644 --- a/packages/nocodb/src/lib/dataMapper/lib/sql/genRollupSelect.ts +++ b/packages/nocodb/src/lib/dataMapper/lib/sql/genRollupSelect.ts @@ -1,25 +1,32 @@ -import Knex from "knex"; +import Knex from 'knex'; -export default function ({knex, rollup,}: { knex: Knex, rollup: any }) { +export default function({ knex, rollup }: { knex: Knex; rollup: any }) { switch (rollup.type) { case 'hm': - return knex(rollup.rltn) [rollup.fn]?.(knex.ref(`${rollup.rltn}.${rollup.rlcn}`)) .where( - knex.ref(`${rollup.tn}.${rollup.cn}`), '=', knex.ref(`${rollup.rtn}.${rollup.rcn}`) - ) + knex.ref(`${rollup.tn}.${rollup.cn}`), + '=', + knex.ref(`${rollup.rtn}.${rollup.rcn}`) + ); break; case 'mm': - return knex(rollup.rltn) [rollup.fn]?.(knex.ref(`${rollup.rltn}.${rollup.rlcn}`)) - .innerJoin(rollup.vtn, knex.ref(`${rollup.vtn}.${rollup.vrcn}`), '=', knex.ref(`${rollup.rtn}.${rollup.rcn}`)) - .where(knex.ref(`${rollup.vtn}.${rollup.vcn}`), '=', knex.ref(`${rollup.tn}.${rollup.cn}`)) - + .innerJoin( + rollup.vtn, + knex.ref(`${rollup.vtn}.${rollup.vrcn}`), + '=', + knex.ref(`${rollup.rtn}.${rollup.rcn}`) + ) + .where( + knex.ref(`${rollup.vtn}.${rollup.vcn}`), + '=', + knex.ref(`${rollup.tn}.${rollup.cn}`) + ); default: - throw Error(`Unsupported relation type '${rollup.type}'`) + throw Error(`Unsupported relation type '${rollup.type}'`); } } - diff --git a/packages/nocodb/src/lib/dataMapper/lib/sql/mapFunctionName.ts b/packages/nocodb/src/lib/dataMapper/lib/sql/mapFunctionName.ts index 3edf8faaff..0c2bb927bc 100644 --- a/packages/nocodb/src/lib/dataMapper/lib/sql/mapFunctionName.ts +++ b/packages/nocodb/src/lib/dataMapper/lib/sql/mapFunctionName.ts @@ -1,23 +1,22 @@ -import {XKnex} from "../../index"; -import mssql from "./functionMappings/mssql"; -import mysql from "./functionMappings/mysql"; -import pg from "./functionMappings/pg"; -import sqlite from "./functionMappings/sqlite"; -import {QueryBuilder} from "knex"; +import { XKnex } from '../../index'; +import mssql from './functionMappings/mssql'; +import mysql from './functionMappings/mysql'; +import pg from './functionMappings/pg'; +import sqlite from './functionMappings/sqlite'; +import { QueryBuilder } from 'knex'; export interface MapFnArgs { - pt: any, - aliasToCol: { [alias: string]: string }, - knex: XKnex, - alias: string, - a?: string, - fn: (...args: any) => QueryBuilder | any, - colAlias: string, - prevBinaryOp?: any + pt: any; + aliasToCol: { [alias: string]: string }; + knex: XKnex; + alias: string; + a?: string; + fn: (...args: any) => QueryBuilder | any; + colAlias: string; + prevBinaryOp?: any; } const mapFunctionName = (args: MapFnArgs): any => { - const name = args.pt.callee.name; let val; @@ -40,11 +39,10 @@ const mapFunctionName = (args: MapFnArgs): any => { } if (typeof val === 'function') { - return val(args) + return val(args); } else if (typeof val === 'string') { args.pt.callee.name = val; } -} - +}; export default mapFunctionName; diff --git a/packages/nocodb/src/lib/index.ts b/packages/nocodb/src/lib/index.ts index 70470e9ec4..1125daf349 100644 --- a/packages/nocodb/src/lib/index.ts +++ b/packages/nocodb/src/lib/index.ts @@ -1,15 +1,10 @@ -import Noco from './noco/Noco' +import Noco from './noco/Noco'; import XcTry from './noco/nc.try'; import NcConfigFactory from './utils/NcConfigFactory'; - export default Noco; -export { - Noco, - NcConfigFactory, - XcTry -}; +export { Noco, NcConfigFactory, XcTry }; /** * @copyright Copyright (c) 2021, Xgene Cloud Ltd diff --git a/packages/nocodb/src/lib/migrator/SqlMigrator/lib/KnexMigrator.ts b/packages/nocodb/src/lib/migrator/SqlMigrator/lib/KnexMigrator.ts index b2d35c7dad..2ef14a4f89 100644 --- a/packages/nocodb/src/lib/migrator/SqlMigrator/lib/KnexMigrator.ts +++ b/packages/nocodb/src/lib/migrator/SqlMigrator/lib/KnexMigrator.ts @@ -1,25 +1,23 @@ -import fs from "fs"; -import path from "path"; -import {promisify} from "util"; +import fs from 'fs'; +import path from 'path'; +import { promisify } from 'util'; -import glob from "glob"; -import Handlebars from "handlebars"; -import mkdirp from "mkdirp"; -import {SqlClientFactory} from 'nc-help'; -import rmdir from "rmdir"; +import glob from 'glob'; +import Handlebars from 'handlebars'; +import mkdirp from 'mkdirp'; +import { SqlClientFactory } from 'nc-help'; +import rmdir from 'rmdir'; +import Debug from '../../util/Debug'; +import Result from '../../util/Result'; +import Emit from '../../util/emit'; +import * as fileHelp from '../../util/file.help'; -import Debug from "../../util/Debug"; -import Result from "../../util/Result"; -import Emit from "../../util/emit"; -import * as fileHelp from "../../util/file.help"; - - -import SqlMigrator from "./SqlMigrator"; -import NcConfigFactory from "../../../utils/NcConfigFactory"; +import SqlMigrator from './SqlMigrator'; +import NcConfigFactory from '../../../utils/NcConfigFactory'; const evt = new Emit(); -const log = new Debug("KnexMigrator"); +const log = new Debug('KnexMigrator'); /** * Class to create an instance of KnexMigrator * @@ -27,7 +25,6 @@ const log = new Debug("KnexMigrator"); * @extends {SqlMigrator} */ export default class KnexMigrator extends SqlMigrator { - // @ts-ignore private projectObj: any; // @ts-ignore @@ -50,7 +47,7 @@ export default class KnexMigrator extends SqlMigrator { emit(data, _args?) { log.api(data); - evt.evt.emit("UI", { + evt.evt.emit('UI', { status: 0, data: `Migrator : ${data}` }); @@ -58,7 +55,7 @@ export default class KnexMigrator extends SqlMigrator { emitW(data, _args?) { log.warn(data); - evt.evt.emit("UI", { + evt.evt.emit('UI', { status: 1, data }); @@ -66,7 +63,7 @@ export default class KnexMigrator extends SqlMigrator { emitE(data, _args?) { log.error(data); - evt.evt.emit("UI", { + evt.evt.emit('UI', { status: -1, data }); @@ -80,17 +77,23 @@ export default class KnexMigrator extends SqlMigrator { * @memberof KnexMigrator */ _getWorkingEnvDir(args) { - return path.join(this.toolDir, 'nc', this.project.id, args.dbAlias, 'migrations'); + return path.join( + this.toolDir, + 'nc', + this.project.id, + args.dbAlias, + 'migrations' + ); } async _initAllEnvOnFilesystem() { - const {envs} = this.project; + const { envs } = this.project; // working env will have all databases - const {workingEnv} = this.project; + const { workingEnv } = this.project; for (let i = 0; i < envs[workingEnv].db.length; ++i) { - const {dbAlias} = envs[workingEnv].db[i].meta; + const { dbAlias } = envs[workingEnv].db[i].meta; await this._initDbOnFs({ dbAlias }); @@ -98,13 +101,13 @@ export default class KnexMigrator extends SqlMigrator { } async _cleanDbAliasOnFilesystem(args) { - const {envs} = this.project; + const { envs } = this.project; // working env will have all databases const toCleanEnv = args.env || this.project.workingEnv; for (let i = 0; i < envs[toCleanEnv].db.length; ++i) { - const {dbAlias} = envs[toCleanEnv].db[i].meta; + const { dbAlias } = envs[toCleanEnv].db[i].meta; await this._cleanFs({ dbAlias @@ -114,74 +117,147 @@ export default class KnexMigrator extends SqlMigrator { async _initDbOnFs(args) { this.emit( - "Creating folder: ", + 'Creating folder: ', path.join(this.toolDir, 'nc', this.project.id, args.dbAlias, 'migrations') ); try { await promisify(mkdirp)( - path.join(this.toolDir, 'nc', this.project.id, args.dbAlias, 'migrations') + path.join( + this.toolDir, + 'nc', + this.project.id, + args.dbAlias, + 'migrations' + ) ); // @ts-ignore const dirStat = await promisify(fs.stat)( - path.join(this.toolDir, 'nc', this.project.id, args.dbAlias, 'migrations') + path.join( + this.toolDir, + 'nc', + this.project.id, + args.dbAlias, + 'migrations' + ) ); await promisify(mkdirp)( - path.join(this.toolDir, 'nc', this.project.id, args.dbAlias, this.project.meta.metaFolder || 'meta') + path.join( + this.toolDir, + 'nc', + this.project.id, + args.dbAlias, + this.project.meta.metaFolder || 'meta' + ) ); this.emit( - "Creating folder: ", - path.join(this.toolDir, 'nc', this.project.id, args.dbAlias, this.project.meta.seedsFolder) + 'Creating folder: ', + path.join( + this.toolDir, + 'nc', + this.project.id, + args.dbAlias, + this.project.meta.seedsFolder + ) ); await promisify(mkdirp)( - path.join(this.toolDir, 'nc', this.project.id, args.dbAlias, this.project.meta.seedsFolder) + path.join( + this.toolDir, + 'nc', + this.project.id, + args.dbAlias, + this.project.meta.seedsFolder + ) ); this.emit( - "Creating folder: ", - path.join(this.toolDir, 'nc', this.project.id, args.dbAlias, this.project.meta.queriesFolder) + 'Creating folder: ', + path.join( + this.toolDir, + 'nc', + this.project.id, + args.dbAlias, + this.project.meta.queriesFolder + ) ); await promisify(mkdirp)( - path.join(this.toolDir, 'nc', this.project.id, args.dbAlias, this.project.meta.queriesFolder) + path.join( + this.toolDir, + 'nc', + this.project.id, + args.dbAlias, + this.project.meta.queriesFolder + ) ); this.emit( - "Creating folder: ", - path.join(this.toolDir, 'nc', this.project.id, this.project.meta.apisFolder) + 'Creating folder: ', + path.join( + this.toolDir, + 'nc', + this.project.id, + this.project.meta.apisFolder + ) ); await promisify(mkdirp)( - path.join(this.toolDir, 'nc', this.project.id, this.project.meta.apisFolder) + path.join( + this.toolDir, + 'nc', + this.project.id, + this.project.meta.apisFolder + ) ); - await promisify(mkdirp)( - path.join(this.toolDir, 'nc', this.project.id, args.dbAlias, this.project.meta.metaFolder || 'meta') + path.join( + this.toolDir, + 'nc', + this.project.id, + args.dbAlias, + this.project.meta.metaFolder || 'meta' + ) ); // @ts-ignore const metaStat = await promisify(fs.stat)( - path.join(this.toolDir, 'nc', this.project.id, args.dbAlias, this.project.meta.metaFolder || 'meta') + path.join( + this.toolDir, + 'nc', + this.project.id, + args.dbAlias, + this.project.meta.metaFolder || 'meta' + ) ); - } catch (e) { log.debug( - "Error creating folders (migrations, apis, seeds, queries):", - path.join(this.toolDir, 'nc', this.project.id, args.dbAlias, 'migrations') + 'Error creating folders (migrations, apis, seeds, queries):', + path.join( + this.toolDir, + 'nc', + this.project.id, + args.dbAlias, + 'migrations' + ) ); } } async _cleanFs(args) { - this.emit("Removing folder: ", path.join(this.toolDir, 'nc', this.project.id, args.dbAlias)); + this.emit( + 'Removing folder: ', + path.join(this.toolDir, 'nc', this.project.id, args.dbAlias) + ); try { - await promisify(rmdir)(path.join(this.toolDir, 'nc', this.project.id, args.dbAlias)); + await promisify(rmdir)( + path.join(this.toolDir, 'nc', this.project.id, args.dbAlias) + ); } catch (e) { log.debug( - "Error removing folder:", + 'Error removing folder:', path.join(this.toolDir, 'nc', this.project.id, args.dbAlias), e ); @@ -192,24 +268,23 @@ export default class KnexMigrator extends SqlMigrator { try { // projJsonFilePath = `${path.join(this.toolDir, "config.xc.json")}`; - log.debug("_readProjectJson", projJsonFilePath); + log.debug('_readProjectJson', projJsonFilePath); const exists = await promisify(fs.exists)(projJsonFilePath); if (exists) { // this.project = await promisify(jsonfile.readFile)(projJsonFilePath); - this.project = await promisify(fs.readFile)( - projJsonFilePath, - "utf8" - ); + this.project = await promisify(fs.readFile)(projJsonFilePath, 'utf8'); this.project = JSON.parse(this.project, (_key, value) => { - return typeof value === 'string' ? Handlebars.compile(value, {noEscape: true})(process.env) : value; + return typeof value === 'string' + ? Handlebars.compile(value, { noEscape: true })(process.env) + : value; }); - this.project.folder = this.toolDir || path.dirname(projJsonFilePath) + this.project.folder = this.toolDir || path.dirname(projJsonFilePath); } else { - throw new Error("Project file should have got created"); + throw new Error('Project file should have got created'); } } catch (e) { - log.debug("error in _readProjectJson: ", e); + log.debug('error in _readProjectJson: ', e); } } @@ -219,48 +294,48 @@ export default class KnexMigrator extends SqlMigrator { args.folder = this.toolDir; } - const projJsonFilePath = `${path.join(args.folder, "config.xc.json")}`; + const projJsonFilePath = `${path.join(args.folder, 'config.xc.json')}`; log.debug(args, projJsonFilePath); const exists = await promisify(fs.exists)(projJsonFilePath); if (exists) { await this._readProjectJson(projJsonFilePath); - this.emit("Migrator for project initalised successfully"); + this.emit('Migrator for project initalised successfully'); } else if (NcConfigFactory.hasDbUrl()) { this.project = NcConfigFactory.make(); } else { - args.type = args.type || "sqlite"; + args.type = args.type || 'sqlite'; - let freshProject = require("./templates/sqlite.template"); + let freshProject = require('./templates/sqlite.template'); if (args.projectJson) { freshProject = args.projectJson; } else { switch (args.type) { - case "mysql": - case "mysql2": - freshProject = require("./templates/mysql.template.js"); + case 'mysql': + case 'mysql2': + freshProject = require('./templates/mysql.template.js'); break; - case "pg": - freshProject = require("./templates/pg.template.js"); + case 'pg': + freshProject = require('./templates/pg.template.js'); break; - case "mssql": - freshProject = require("./templates/mssql.template.js"); + case 'mssql': + freshProject = require('./templates/mssql.template.js'); break; - case "oracle": - freshProject = require("./templates/oracle.template.js"); + case 'oracle': + freshProject = require('./templates/oracle.template.js'); break; - case "sqlite": - freshProject = require("./templates/sqlite.template.js"); + case 'sqlite': + freshProject = require('./templates/sqlite.template.js'); break; default: - throw new Error("Unknown database option provided"); + throw new Error('Unknown database option provided'); break; } } @@ -275,7 +350,7 @@ export default class KnexMigrator extends SqlMigrator { this.emit(`Project folder created successfully: ${args.folder}`); - const newProjectJsonPath = path.join(args.folder, "config.xc.json"); + const newProjectJsonPath = path.join(args.folder, 'config.xc.json'); if (args.title) { freshProject.title = args.title; @@ -284,7 +359,7 @@ export default class KnexMigrator extends SqlMigrator { await promisify(fs.writeFile)( newProjectJsonPath, JSON.stringify(freshProject, null, 2), - "utf-8" + 'utf-8' ); this.emit(`Project json created successfully: ${newProjectJsonPath}`); @@ -298,39 +373,32 @@ export default class KnexMigrator extends SqlMigrator { } async _initDbWithSql(connectionConfig) { - const sqlClient = connectionConfig.sqlClient || SqlClientFactory.create(connectionConfig); - if (connectionConfig.client === "oracledb") { + const sqlClient = + connectionConfig.sqlClient || SqlClientFactory.create(connectionConfig); + if (connectionConfig.client === 'oracledb') { this.emit( - `${connectionConfig.client}: Creating DB if not exists ${ - connectionConfig.connection.user - }` + `${connectionConfig.client}: Creating DB if not exists ${connectionConfig.connection.user}` ); await sqlClient.createDatabaseIfNotExists({ database: connectionConfig.connection.user }); - } else if (connectionConfig.client !== "sqlite3") { + } else if (connectionConfig.client !== 'sqlite3') { this.emit( - `${connectionConfig.client}: Creating DB if not exists ${ - connectionConfig.connection.database - }` + `${connectionConfig.client}: Creating DB if not exists ${connectionConfig.connection.database}` ); await sqlClient.createDatabaseIfNotExists({ database: connectionConfig.connection.database }); } else { this.emit( - `${connectionConfig.client}: Creating DB if not exists ${ - connectionConfig.connection.connection.filename - }` + `${connectionConfig.client}: Creating DB if not exists ${connectionConfig.connection.connection.filename}` ); await sqlClient.createDatabaseIfNotExists({ database: connectionConfig.connection.connection.filename }); } - this.emit( - `Creating Table if not exists in ${connectionConfig.meta.tn}` - ); + this.emit(`Creating Table if not exists in ${connectionConfig.meta.tn}`); if (!('NC_MIGRATIONS_DISABLED' in process.env)) { await sqlClient.createTableIfNotExists({ @@ -353,12 +421,12 @@ export default class KnexMigrator extends SqlMigrator { async _cleanDbWithSql(connectionConfig) { const sqlClient = SqlClientFactory.create(connectionConfig); - if (connectionConfig.client === "oracledb") { + if (connectionConfig.client === 'oracledb') { this.emit(`Dropping DB : ${connectionConfig.connection.user}`); await sqlClient.dropDatabase({ database: connectionConfig.connection.user }); - } else if (connectionConfig.client === "sqlite3") { + } else if (connectionConfig.client === 'sqlite3') { this.emit( `Dropping DB : ${connectionConfig.connection.connection.filename}` ); @@ -376,35 +444,30 @@ export default class KnexMigrator extends SqlMigrator { } async _initEnvDbsWithSql(env, dbAlias = null, sqlClient = null) { - - const {envs} = this.project; + const { envs } = this.project; this.emit(`Initialising env: ${env}`); for (let i = 0; i < this.project.envs[env].db.length; ++i) { - const connectionConfig = envs[env].db[i]; /* if no dbAlias - init all dbs in env. Else check if it matches the one sent in args */ if (!dbAlias || dbAlias === envs[env].db[i].meta.dbAlias) { - await this._initDbWithSql({...connectionConfig, sqlClient}); + await this._initDbWithSql({ ...connectionConfig, sqlClient }); } } } - async _initAllEnvDbsWithSql(_args) { - // const env = ''; for (const env in this.project.envs) { - await this._initEnvDbsWithSql(env, null) + await this._initEnvDbsWithSql(env, null); } - } async _cleanAllEnvDbs() { - const {envs} = this.project; + const { envs } = this.project; // removes all envs for (const env in envs) { for (let i = 0; i < envs[env].db.length; ++i) { @@ -412,16 +475,14 @@ export default class KnexMigrator extends SqlMigrator { await this._cleanDbWithSql(connectionConfig); } } - } - async _cleanEnvDbsWithSql(args) { - const {envs} = this.project; + const { envs } = this.project; if (args.env) { // remove environment from argument - const {env} = args; + const { env } = args; for (let i = 0; i < envs[env].db.length; ++i) { const connectionConfig = envs[env].db[i]; @@ -437,7 +498,7 @@ export default class KnexMigrator extends SqlMigrator { } } } else { - this._cleanAllEnvDbs() + this._cleanAllEnvDbs(); } } @@ -453,8 +514,6 @@ export default class KnexMigrator extends SqlMigrator { } async _migrationsUp(args): Promise { - - const result = new Result(); result.data.object = {}; @@ -464,37 +523,38 @@ export default class KnexMigrator extends SqlMigrator { return result; } try { - const {onlyList} = args; - let {migrationSteps} = args; + const { onlyList } = args; + let { migrationSteps } = args; if (!migrationSteps && !args.file) { result.code = -1; result.message = - "Neither num of steps nor file is specified for migration"; - log.debug("Neither num of steps nor file is specified for migration"); - log.debug("See help"); + 'Neither num of steps nor file is specified for migration'; + log.debug('Neither num of steps nor file is specified for migration'); + log.debug('See help'); return result; } /** ************** START : get files and migrations *************** */ - // get all evolutions from fs - // const files = await promisify(glob)(args.upFilesPattern); - // const filesDown = await promisify(glob)(args.downFilesPattern); + // get all evolutions from fs + // const files = await promisify(glob)(args.upFilesPattern); + // const filesDown = await promisify(glob)(args.downFilesPattern); let files; let filesDown; if (this.metaDb) { - filesDown = files = await this.metaDb('nc_migrations').where({ - project_id: this.project_id, - db_alias: args.dbAlias - }).orderBy('id', 'asc') + filesDown = files = await this.metaDb('nc_migrations') + .where({ + project_id: this.project_id, + db_alias: args.dbAlias + }) + .orderBy('id', 'asc'); } else { files = await promisify(glob)(args.upFilesPattern); filesDown = await promisify(glob)(args.downFilesPattern); } - // get evolutions from sql const connection = this._getSqlConnectionFromDbAlias( args.dbAlias, @@ -502,16 +562,20 @@ export default class KnexMigrator extends SqlMigrator { ); const sqlClient = args.sqlClient || SqlClientFactory.create(connection); - let migrations = await sqlClient.selectAll(sqlClient.getTnPath(connection.meta.tn)); + let migrations = await sqlClient.selectAll( + sqlClient.getTnPath(connection.meta.tn) + ); if (this.suffix) { - migrations = migrations.filter(m => m.title.includes(this.suffix)) + migrations = migrations.filter(m => m.title.includes(this.suffix)); } /** ************** END : get files and migrations *************** */ if (files.length === migrations.length) { - this.emit(`Evolutions are upto date for ' ${args.env} : ${args.dbAlias} '`); + this.emit( + `Evolutions are upto date for ' ${args.env} : ${args.dbAlias} '` + ); for (let i = 0; i < migrations.length; ++i) { result.data.object.list.push({ title: migrations[i].title, @@ -523,7 +587,9 @@ export default class KnexMigrator extends SqlMigrator { result.data.object.pending = files.length - migrations.length; } else if (files.length > migrations.length || onlyList) { this.emit( - `Number of evolutions pending for '${args.env}:${args.dbAlias}': '${files.length - migrations.length}'` + `Number of evolutions pending for '${args.env}:${ + args.dbAlias + }': '${files.length - migrations.length}'` ); result.data.object.pending = files.length - migrations.length; @@ -533,7 +599,7 @@ export default class KnexMigrator extends SqlMigrator { if (migrations.length !== 0) { // get last evolution that was made const lastEvolution = migrations[migrations.length - 1]; - let i = 0 + let i = 0; // find the index of the last evolution in evolution list of files for (; i < files.length; ++i) { if (this.metaDb) { @@ -554,7 +620,6 @@ export default class KnexMigrator extends SqlMigrator { /** ************** END : Find file index to begin migrations *************** */ try { - /** ************** START : calculate migration steps from filename *************** */ if (!migrationSteps) { let fileFound = 0; @@ -592,8 +657,8 @@ export default class KnexMigrator extends SqlMigrator { } } else { for (let i = 0; i < fileIndex; ++i) { - const fileParts = files[i].split("/"); - const downFileParts = filesDown[i].split("/"); + const fileParts = files[i].split('/'); + const downFileParts = filesDown[i].split('/'); result.data.object.list.push({ title: fileParts[fileParts.length - 1], titleDown: downFileParts[fileParts.length - 1], @@ -619,10 +684,10 @@ export default class KnexMigrator extends SqlMigrator { fileName = files[i].title; fileNameDown = files[i].title_down; } else { - const splits = files[i].split("/"); + const splits = files[i].split('/'); fileName = splits[splits.length - 1]; - const splitsDown = filesDown[i].split("/"); + const splitsDown = filesDown[i].split('/'); fileNameDown = splitsDown[splitsDown.length - 1]; } @@ -639,15 +704,15 @@ export default class KnexMigrator extends SqlMigrator { if (this.metaDb) { upStatement = files[i].up; } else { - upStatement = await promisify(fs.readFile)( - files[i], - "utf8" - ); + upStatement = await promisify(fs.readFile)(files[i], 'utf8'); } if (args.sqlContentMigrate) { // Split base on comments which starts with `xc` , eg : /* xc :test */ , /*xc*/ - upStatements.push(...upStatement.split(/\/\*\s*xc[\s\S]*?\s*\*\//).filter(s => s.trim())); - + upStatements.push( + ...upStatement + .split(/\/\*\s*xc[\s\S]*?\s*\*\//) + .filter(s => s.trim()) + ); } metaTableInserts.push({ @@ -661,36 +726,35 @@ export default class KnexMigrator extends SqlMigrator { } if (onlyList) { - } else { const vm = this; const trx = await sqlClient.knex.transaction(); try { - for (const query of upStatements) { await trx.raw(query); vm.emit(`'${query}' : Executed SQL query`); } for (const data of metaTableInserts) { await trx(sqlClient.getTnPath(connection.meta.tn)).insert(data); - vm.emit(`'${data.title}' : Updating bookkeeping of SQL UP migration - done`); + vm.emit( + `'${data.title}' : Updating bookkeeping of SQL UP migration - done` + ); } await trx.commit(); //console.log('========== success ') - } catch (error) { await trx.rollback(); - vm.emitW(`Migration operation failed, Database restored to previous state`); + vm.emitW( + `Migration operation failed, Database restored to previous state` + ); log.ppe(error, ''); throw error; } } - /** ************** END : Apply migrations *************** */ - } catch (e) { this.emitE(`Error while migrating up : ${e.code} and ${e.message}`); log.debug(e); @@ -698,10 +762,10 @@ export default class KnexMigrator extends SqlMigrator { } } else { result.data.object.pending = -1; - this.emitE("Evolutions are dirty - reset the whole thing please"); + this.emitE('Evolutions are dirty - reset the whole thing please'); } } catch (e) { - log.debug("Error in evolutionUp", e); + log.debug('Error in evolutionUp', e); result.code = -1; result.message = e.message; throw e; @@ -711,34 +775,35 @@ export default class KnexMigrator extends SqlMigrator { } async _migrationsDown(args) { - if (process.env.NC_MIGRATIONS_DISABLED) { - return + return; } // const {env} = args; - let {migrationSteps} = args; - const {onlyList} = args; + let { migrationSteps } = args; + const { onlyList } = args; if (!migrationSteps && !args.file) { - log.debug("Neither num of steps nor file is specified for migartion"); - log.debug("See help"); + log.debug('Neither num of steps nor file is specified for migartion'); + log.debug('See help'); return; } if (args.onlyList) { - log.debug("Pending migration list:"); + log.debug('Pending migration list:'); } else { - log.debug("Migrating Down:"); + log.debug('Migrating Down:'); } try { let files; if (this.metaDb) { - files = await this.metaDb('nc_migrations').where({ - project_id: this.project_id, - db_alias: args.dbAlias - }).orderBy('title', 'asc') + files = await this.metaDb('nc_migrations') + .where({ + project_id: this.project_id, + db_alias: args.dbAlias + }) + .orderBy('title', 'asc'); } else { // get all evolutions from fs files = await promisify(glob)(args.downFilesPattern); @@ -749,11 +814,12 @@ export default class KnexMigrator extends SqlMigrator { args.env ); const sqlClient = SqlClientFactory.create(connection); - const migrations = await sqlClient.selectAll(sqlClient.getTnPath(connection.meta.tn)); + const migrations = await sqlClient.selectAll( + sqlClient.getTnPath(connection.meta.tn) + ); if (migrations.length) { try { - if (!migrationSteps) { let fileFound = 0; for (let i = migrations.length - 1; i >= 0; --i) { @@ -789,7 +855,7 @@ export default class KnexMigrator extends SqlMigrator { if (this.metaDb) { fileName = files[i].title_down; } else { - const splits = files[i].split("/"); + const splits = files[i].split('/'); fileName = splits[splits.length - 1]; } if (onlyList) { @@ -800,18 +866,18 @@ export default class KnexMigrator extends SqlMigrator { if (this.metaDb) { downStatement = files[i].down; } else { - downStatement = await promisify(fs.readFile)( - files[i], - "utf8" - ); + downStatement = await promisify(fs.readFile)(files[i], 'utf8'); } if (args.sqlContentMigrate) - downStatements.push(...downStatement.split(/\/\*\s*xc[\s\S]*?\s*\*\//).filter(s => s.trim())); + downStatements.push( + ...downStatement + .split(/\/\*\s*xc[\s\S]*?\s*\*\//) + .filter(s => s.trim()) + ); metaDownDeletes.push({ titleDown: fileName }); - } } @@ -819,20 +885,21 @@ export default class KnexMigrator extends SqlMigrator { const trx = await sqlClient.knex.transaction(); try { - for (const query of downStatements) { await trx.raw(query).transacting(trx); vm.emit(`'${query}' : Executed SQL query`); } for (const condition of metaDownDeletes) { - vm.emit(`'${condition.titleDown}' : Updating bookkeeping of SQL DOWN migration - done`); - await trx(sqlClient.getTnPath(connection.meta.tn)).where(condition).del(); + vm.emit( + `'${condition.titleDown}' : Updating bookkeeping of SQL DOWN migration - done` + ); + await trx(sqlClient.getTnPath(connection.meta.tn)) + .where(condition) + .del(); } - await trx.commit(); //console.log('========== success '); - } catch (error) { await trx.rollback(); vm.emitW( @@ -846,7 +913,7 @@ export default class KnexMigrator extends SqlMigrator { } } } catch (e) { - log.debug("Error in evolutionUp", e); + log.debug('Error in evolutionUp', e); } } @@ -862,7 +929,6 @@ export default class KnexMigrator extends SqlMigrator { * @memberof KnexMigrator */ async init(args) { - // if (this.metaDb) { // return; // } @@ -876,7 +942,7 @@ export default class KnexMigrator extends SqlMigrator { // await this._initProjectJsonFile(args); await this._initAllEnvOnFilesystem(); } catch (e) { - log.debug("Error in creating migration project:", e); + log.debug('Error in creating migration project:', e); throw e; } } @@ -888,12 +954,10 @@ export default class KnexMigrator extends SqlMigrator { * @memberof KnexMigrator */ async sync(args: any = {}) { - const func = this.sync.name; log.api(`${func}:args:`, args); try { - // if (!this.project) { // await this._readProjectJson(path.join(args.folder, "config.xc.json")); // } @@ -912,9 +976,8 @@ export default class KnexMigrator extends SqlMigrator { } else { await this._initEnvDbsWithSql(args.env, args.dbAlias, args.sqlClient); } - } catch (e) { - log.debug("Error in creating migration project:", e); + log.debug('Error in creating migration project:', e); throw e; } } @@ -946,9 +1009,9 @@ export default class KnexMigrator extends SqlMigrator { await this._cleanEnvDbsWithSql(args); - log.debug("Cleaning all Databases"); + log.debug('Cleaning all Databases'); } catch (e) { - log.debug("Error in cleaning migration project:", e); + log.debug('Error in cleaning migration project:', e); throw e; } } @@ -981,7 +1044,6 @@ export default class KnexMigrator extends SqlMigrator { const upFileName = fileHelp.getFilenameForUp(prefix); const downFileName = fileHelp.getFilenameForDown(prefix); if (this.metaDb) { - await this.metaDb('nc_migrations').insert({ project_id: this.project_id, db_alias: args.dbAlias, @@ -989,19 +1051,18 @@ export default class KnexMigrator extends SqlMigrator { down: '', title: upFileName, title_down: downFileName - }) - + }); } else { // create files await promisify(fs.writeFile)( path.join(this._getWorkingEnvDir(args), upFileName), - "", - "utf-8" + '', + 'utf-8' ); await promisify(fs.writeFile)( path.join(this._getWorkingEnvDir(args), downFileName), - "", - "utf-8" + '', + 'utf-8' ); } @@ -1019,7 +1080,6 @@ export default class KnexMigrator extends SqlMigrator { } } - /** * Creates up and down migration files within migration folders * @@ -1036,7 +1096,6 @@ export default class KnexMigrator extends SqlMigrator { log.api(`${func}:args:`, args); try { - // if (!this.project) { // await this._readProjectJson(path.join(args.folder, "config.xc.json")); // } @@ -1044,7 +1103,6 @@ export default class KnexMigrator extends SqlMigrator { // if (NcConfigFactory.hasDbUrl()) { // this.project = NcConfigFactory.make(); // } - /** * * Call migrationsDown() here @@ -1052,7 +1110,6 @@ export default class KnexMigrator extends SqlMigrator { * Then delete the records * */ - } catch (e) { log.debug(e); throw e; @@ -1076,7 +1133,6 @@ export default class KnexMigrator extends SqlMigrator { * @memberof KnexMigrator */ async migrationsUp(args: any = {}) { - const func = this.migrationsUp.name; // const result = new Result(); log.api(`${func}:args:`, args); @@ -1095,9 +1151,23 @@ export default class KnexMigrator extends SqlMigrator { migrationSteps: args.migrationSteps, file: args.file, onlyList: args.onlyList, - upFilesPattern: path.join(this.toolDir, 'nc', this.project.id, args.dbAlias, 'migrations', '*.up.sql'), - downFilesPattern: path.join(this.toolDir, 'nc', this.project.id, args.dbAlias, 'migrations', '*.down.sql'), - tn: this._getEvolutionsTablename(args),//`${this.toolDir}`, + upFilesPattern: path.join( + this.toolDir, + 'nc', + this.project.id, + args.dbAlias, + 'migrations', + '*.up.sql' + ), + downFilesPattern: path.join( + this.toolDir, + 'nc', + this.project.id, + args.dbAlias, + 'migrations', + '*.down.sql' + ), + tn: this._getEvolutionsTablename(args), //`${this.toolDir}`, sqlContentMigrate: args.sqlContentMigrate, sqlClient: args.sqlClient }); @@ -1136,9 +1206,23 @@ export default class KnexMigrator extends SqlMigrator { migrationSteps: args.migrationSteps, onlyList: args.onlyList, file: args.file, - upFilesPattern: path.join(this.toolDir, 'nc', this.project.id, args.dbAlias, 'migrations', '*.up.sql'), - downFilesPattern: path.join(this.toolDir, 'nc', this.project.id, args.dbAlias, 'migrations', '*.down.sql'), - tn: this._getEvolutionsTablename(args),//`_evolutions`, + upFilesPattern: path.join( + this.toolDir, + 'nc', + this.project.id, + args.dbAlias, + 'migrations', + '*.up.sql' + ), + downFilesPattern: path.join( + this.toolDir, + 'nc', + this.project.id, + args.dbAlias, + 'migrations', + '*.down.sql' + ), + tn: this._getEvolutionsTablename(args), //`_evolutions`, sqlContentMigrate: args.sqlContentMigrate }); } @@ -1172,32 +1256,38 @@ export default class KnexMigrator extends SqlMigrator { // this.project = NcConfigFactory.make(); // } - let upStatement = ""; - let downStatement = ""; + let upStatement = ''; + let downStatement = ''; for (let i = 0; i < args.upStatement.length; i++) { upStatement = `${upStatement + args.upStatement[i].sql};`; } - log.debug("migrationsWrite: created up statement", upStatement); + log.debug('migrationsWrite: created up statement', upStatement); for (let i = 0; i < args.downStatement.length; i++) { downStatement = `${downStatement + args.downStatement[i].sql};`; } - log.debug("migrationsWrite: created downStatement", downStatement); + log.debug('migrationsWrite: created downStatement', downStatement); if (this.metaDb) { - if (await this.metaDb('nc_migrations').where({ - project_id: this.project_id, - db_alias: args.dbAlias, - title: args.up - }).first()) { - await this.metaDb('nc_migrations').update({ - up: upStatement, - down: downStatement, - }).where({ - project_id: this.project_id, - db_alias: args.dbAlias, - title: args.up, - }) + if ( + await this.metaDb('nc_migrations') + .where({ + project_id: this.project_id, + db_alias: args.dbAlias, + title: args.up + }) + .first() + ) { + await this.metaDb('nc_migrations') + .update({ + up: upStatement, + down: downStatement + }) + .where({ + project_id: this.project_id, + db_alias: args.dbAlias, + title: args.up + }); } else { await this.metaDb('nc_migrations').insert({ project_id: this.project_id, @@ -1206,25 +1296,25 @@ export default class KnexMigrator extends SqlMigrator { down: downStatement, title: args.up, title_down: args.down - }) + }); } } else { await promisify(fs.writeFile)( path.join(this._getWorkingEnvDir(args), args.up), upStatement, - "utf-8" + 'utf-8' ); - log.debug("migrationsWrite: wrote to file", args.up); + log.debug('migrationsWrite: wrote to file', args.up); await promisify(fs.writeFile)( path.join(this._getWorkingEnvDir(args), args.down), downStatement, - "utf-8" + 'utf-8' ); } - log.debug("migrationsWrite: wrote to file", args.down); + log.debug('migrationsWrite: wrote to file', args.down); } catch (error) { - log.debug("migrationsWrite error: ", error); + log.debug('migrationsWrite error: ', error); } } @@ -1267,8 +1357,8 @@ export default class KnexMigrator extends SqlMigrator { try { result.data.object = { - up: "", - down: "" + up: '', + down: '' }; // if (!this.project) { @@ -1279,28 +1369,46 @@ export default class KnexMigrator extends SqlMigrator { // this.project = NcConfigFactory.make(); // } if (this.metaDb) { - const migration = await this.metaDb('nc_migrations').where({ - db_alias: args.dbAlias, - project_id: this.project.id, - title: args.title - }).first(); - + const migration = await this.metaDb('nc_migrations') + .where({ + db_alias: args.dbAlias, + project_id: this.project.id, + title: args.title + }) + .first(); result.data.object.up = migration.up; result.data.object.down = migration.down; } else { - const upFilePath = path.join(this.toolDir, 'nc', this.project.id, args.dbAlias, 'migrations', args.title); - const downFilePath = path.join(this.toolDir, 'nc', this.project.id, args.dbAlias, 'migrations', args.titleDown); + const upFilePath = path.join( + this.toolDir, + 'nc', + this.project.id, + args.dbAlias, + 'migrations', + args.title + ); + const downFilePath = path.join( + this.toolDir, + 'nc', + this.project.id, + args.dbAlias, + 'migrations', + args.titleDown + ); - result.data.object.up = await promisify(fs.readFile)(upFilePath, "utf8"); + result.data.object.up = await promisify(fs.readFile)( + upFilePath, + 'utf8' + ); result.data.object.down = await promisify(fs.readFile)( downFilePath, - "utf8" + 'utf8' ); } - console.log("migrationsToSql", result.data.object); + console.log('migrationsToSql', result.data.object); } catch (e) { - console.log("migrationsToSql", e); + console.log('migrationsToSql', e); } return result; @@ -1320,8 +1428,7 @@ export default class KnexMigrator extends SqlMigrator { * @param {String} args.down * @memberof KnexMigrator */ - async migrationsSquash() { - } + async migrationsSquash() {} /** * Migrations Create Manual @@ -1336,19 +1443,17 @@ export default class KnexMigrator extends SqlMigrator { * @param {String} args.down * @memberof KnexMigrator */ - async migrationsCreateManually() { - } + async migrationsCreateManually() {} async _writeProjectJson(folder, _json) { const freshProject = ''; - const newProjectJsonPath = path.join(folder, "config.xc.json"); + const newProjectJsonPath = path.join(folder, 'config.xc.json'); await promisify(fs.writeFile)( newProjectJsonPath, JSON.stringify(freshProject, null, 2), - "utf-8" + 'utf-8' ); - } // async _getProjectJson(args) { @@ -1376,13 +1481,11 @@ export default class KnexMigrator extends SqlMigrator { * @returns {Result} */ async migrationsRenameProjectKey(args) { - const func = this.migrationsRenameProjectKey.name; const result: any = new Result(); log.api(`${func}:args:`, args); try { - if (args.key && args.value) { result.code = -1; result.message = `Insufficient number of arguments`; @@ -1396,8 +1499,9 @@ export default class KnexMigrator extends SqlMigrator { await this._writeProjectJson(this.toolDir, this.project); } - this.emitE(`Project key('${args.key}') is set to value successfully ${args.value}`); - + this.emitE( + `Project key('${args.key}') is set to value successfully ${args.value}` + ); } catch (e) { result.code = -1; result.object = e; @@ -1407,10 +1511,8 @@ export default class KnexMigrator extends SqlMigrator { log.api(`${func}:result:`, result); return result; - } - /** * update json * update sqlite @@ -1428,7 +1530,6 @@ export default class KnexMigrator extends SqlMigrator { log.api(`${func}:args:`, args); try { - if (args.folder && args.env) { result.code = -1; result.message = 'Insufficient number of arguments'; @@ -1448,8 +1549,10 @@ export default class KnexMigrator extends SqlMigrator { } await this._writeProjectJson(this.toolDir, this.project); - await this._initEnvDbsWithSql(args.env) - this.emitE(`Environment ' ${args.env} ' created succesfully in project.`); + await this._initEnvDbsWithSql(args.env); + this.emitE( + `Environment ' ${args.env} ' created succesfully in project.` + ); } } catch (e) { result.code = -1; @@ -1461,10 +1564,7 @@ export default class KnexMigrator extends SqlMigrator { return result; } - async migrationsRenameEnv(_sargs) { - - } - + async migrationsRenameEnv(_sargs) {} /** * update json @@ -1477,13 +1577,11 @@ export default class KnexMigrator extends SqlMigrator { * @returns {Promise} */ async migrationsDeleteEnv(args) { - const func = this.migrationsDeleteEnv.name; const result = new Result(); log.api(`${func}:args:`, args); try { - if (args.folder && args.env) { result.code = -1; result.message = 'Insufficient number of arguments'; @@ -1492,18 +1590,14 @@ export default class KnexMigrator extends SqlMigrator { // await this._getProjectJson(args); if (args.env in this.project.envs) { - - await this._cleanEnvDbsWithSql(args) + await this._cleanEnvDbsWithSql(args); delete this.project.envs[args.env]; await this._writeProjectJson(this.toolDir, this.project); this.emitE(`${args.env} deleted`); - } else { - result.code = -1; result.message = `${args.env} doesn't exist in project json`; this.emitE(`${args.env} doesn't exist in project json`); - } } catch (e) { result.code = -1; @@ -1524,13 +1618,11 @@ export default class KnexMigrator extends SqlMigrator { * @returns {Result} */ async migrationsCreateEnvDb(args) { - const func = this.migrationsRenameProjectKey.name; const result = new Result(); log.api(`${func}:args:`, args); try { - if (args.folder && args.env && args.dbAlias) { result.code = -1; result.message = 'Insufficient number of arguments'; @@ -1551,17 +1643,18 @@ export default class KnexMigrator extends SqlMigrator { } if (!found) { - if (args.env === this.project.workingEnv) { // is the input env === dev environment - then push it to last - this.project.envs[args.env].db.push(args.db) + this.project.envs[args.env].db.push(args.db); // TODO : init fs and db } else { - let foundInWorkingEnv = 0; - let i = 0 - for (; i < this.project.envs[this.project.workingEnv].db.length; ++i) { - + let i = 0; + for ( + ; + i < this.project.envs[this.project.workingEnv].db.length; + ++i + ) { const db = this.project.envs[this.project.workingEnv].db[i]; if (db.meta.alias === args.db.meta.alias) { @@ -1572,22 +1665,21 @@ export default class KnexMigrator extends SqlMigrator { if (foundInWorkingEnv) { // in this working env index - place it at the right position - this.project.envs[args.env].db.splice(i, 0, args.db) + this.project.envs[args.env].db.splice(i, 0, args.db); } else { - this.project.envs[args.env].db.push(args.db) + this.project.envs[args.env].db.push(args.db); } - } // TODO : init db for this dbAlias await this._writeProjectJson(this.toolDir, this.project); - - } else { result.code = -1; result.message = 'Database connection already exists with DbAlias'; - this.emitE(`Database connection already exists with DbAlias : ${args.db.meta.dbAlias}`); + this.emitE( + `Database connection already exists with DbAlias : ${args.db.meta.dbAlias}` + ); } } else { result.code = -1; @@ -1620,22 +1712,18 @@ export default class KnexMigrator extends SqlMigrator { throw new Error('Not implemented'); } - - _getEvolutionsTablename({env, dbAlias}) { - const config = this._getSqlConnectionFromDbAlias({env, dbAlias}); + _getEvolutionsTablename({ env, dbAlias }) { + const config = this._getSqlConnectionFromDbAlias({ env, dbAlias }); if (config && config.meta && config.meta.tn) { - return config.meta.tn + return config.meta.tn; } return 'nc_evolutions'; } - private get suffix(): string { - if (this.project?.prefix) - return `_${this.project?.prefix.slice(3,7)}` + if (this.project?.prefix) return `_${this.project?.prefix.slice(3, 7)}`; return ''; } - } /** diff --git a/packages/nocodb/src/lib/migrator/SqlMigrator/lib/SqlMigrator.ts b/packages/nocodb/src/lib/migrator/SqlMigrator/lib/SqlMigrator.ts index d6c4339940..ea5c979cab 100644 --- a/packages/nocodb/src/lib/migrator/SqlMigrator/lib/SqlMigrator.ts +++ b/packages/nocodb/src/lib/migrator/SqlMigrator/lib/SqlMigrator.ts @@ -5,9 +5,7 @@ export default class SqlMigrator { this.project = null; } - init(_project = null) { - - } + init(_project = null) {} // migrationsInit() { // @@ -36,7 +34,6 @@ export default class SqlMigrator { // migrationsDelete() { // // } - } /** diff --git a/packages/nocodb/src/lib/migrator/SqlMigrator/lib/SqlMigratorFactory.ts b/packages/nocodb/src/lib/migrator/SqlMigrator/lib/SqlMigratorFactory.ts index dc53a176bf..6f6d5912b7 100644 --- a/packages/nocodb/src/lib/migrator/SqlMigrator/lib/SqlMigratorFactory.ts +++ b/packages/nocodb/src/lib/migrator/SqlMigrator/lib/SqlMigratorFactory.ts @@ -1,18 +1,18 @@ -import KnexMigrator from "./KnexMigrator"; +import KnexMigrator from './KnexMigrator'; export default class SqlMigratorFactory { static create(args) { switch (args.client) { - case "mysql": - case "mysql2": - case "pg": - case "oracledb": - case "mssql": - case "sqlite3": + case 'mysql': + case 'mysql2': + case 'pg': + case 'oracledb': + case 'mssql': + case 'sqlite3': return new KnexMigrator(); break; default: - throw new Error("Database not supported"); + throw new Error('Database not supported'); break; } } diff --git a/packages/nocodb/src/lib/migrator/SqlMigrator/lib/templates/mssql.template.ts b/packages/nocodb/src/lib/migrator/SqlMigrator/lib/templates/mssql.template.ts index bb1eb1b581..bc1ee116fd 100644 --- a/packages/nocodb/src/lib/migrator/SqlMigrator/lib/templates/mssql.template.ts +++ b/packages/nocodb/src/lib/migrator/SqlMigrator/lib/templates/mssql.template.ts @@ -1,48 +1,48 @@ - - module.exports = { - title: "default", + title: 'default', envs: { _noco: { - api:{}, + api: {}, db: [ { - client: "mssql", + client: 'mssql', connection: { - host: process.env.DOCKER_DB_HOST || "localhost", - port: process.env.DOCKER_DB_PORT ? parseInt(process.env.DOCKER_DB_PORT,10) : null || 1433, - user: "sa", - password: "Password123.", - database: "default_dev" + host: process.env.DOCKER_DB_HOST || 'localhost', + port: process.env.DOCKER_DB_PORT + ? parseInt(process.env.DOCKER_DB_PORT, 10) + : null || 1433, + user: 'sa', + password: 'Password123.', + database: 'default_dev' }, meta: { - tn: "nc_evolutions", - dbAlias: "primary" + tn: 'nc_evolutions', + dbAlias: 'primary' } } ] }, test: { - api:{}, + api: {}, db: [ { - client: "mssql", + client: 'mssql', connection: { - host: DOCKER_DB_HOST || "localhost", + host: DOCKER_DB_HOST || 'localhost', port: DOCKER_DB_PORT ? parseInt(DOCKER_DB_PORT) : null || 1433, - user: "sa", - password: "Password123.", - database: "default_test" + user: 'sa', + password: 'Password123.', + database: 'default_test' }, meta: { - tn: "nc_evolutions", - dbAlias: "primary" + tn: 'nc_evolutions', + dbAlias: 'primary' } } ] } }, - workingEnv: "_noco", + workingEnv: '_noco', meta: { version: '0.5', seedsFolder: 'seeds', diff --git a/packages/nocodb/src/lib/migrator/SqlMigrator/lib/templates/mysql.template.ts b/packages/nocodb/src/lib/migrator/SqlMigrator/lib/templates/mysql.template.ts index 2083ae9fb6..d2524b2ee9 100644 --- a/packages/nocodb/src/lib/migrator/SqlMigrator/lib/templates/mysql.template.ts +++ b/packages/nocodb/src/lib/migrator/SqlMigrator/lib/templates/mysql.template.ts @@ -1,22 +1,20 @@ - - module.exports = { - title: "default", + title: 'default', envs: { _noco: { db: [ { - client: "mysql2", + client: 'mysql2', connection: { - host: process.env.DOCKER_DB_HOST || "localhost", + host: process.env.DOCKER_DB_HOST || 'localhost', port: process.env.DOCKER_DB_PORT || 3306, - user: "root", - password: "password", - database: "default_dev" + user: 'root', + password: 'password', + database: 'default_dev' }, meta: { - tn: "nc_evolutions", - dbAlias: "primary" + tn: 'nc_evolutions', + dbAlias: 'primary' } } ] @@ -25,23 +23,23 @@ module.exports = { api: {}, db: [ { - client: "mysql2", + client: 'mysql2', connection: { - host: DOCKER_DB_HOST || "localhost", + host: DOCKER_DB_HOST || 'localhost', port: DOCKER_DB_PORT || 3306, - user: "root", - password: "password", - database: "default_test" + user: 'root', + password: 'password', + database: 'default_test' }, meta: { - tn: "nc_evolutions", - dbAlias: "primary" + tn: 'nc_evolutions', + dbAlias: 'primary' } } ] } }, - workingEnv: "_noco", + workingEnv: '_noco', meta: { version: '0.5', seedsFolder: 'seeds', diff --git a/packages/nocodb/src/lib/migrator/SqlMigrator/lib/templates/pg.template.ts b/packages/nocodb/src/lib/migrator/SqlMigrator/lib/templates/pg.template.ts index f8e7a760f9..62e0341a47 100644 --- a/packages/nocodb/src/lib/migrator/SqlMigrator/lib/templates/pg.template.ts +++ b/packages/nocodb/src/lib/migrator/SqlMigrator/lib/templates/pg.template.ts @@ -1,22 +1,22 @@ -const {DOCKER_DB_HOST, DOCKER_DB_PORT} = process.env; +const { DOCKER_DB_HOST, DOCKER_DB_PORT } = process.env; module.exports = { - title: "default", + title: 'default', envs: { _noco: { db: [ { - client: "pg", + client: 'pg', connection: { - host: DOCKER_DB_HOST || "localhost", + host: DOCKER_DB_HOST || 'localhost', port: DOCKER_DB_PORT || 5432, - user: "postgres", - password: "password", - database: "default_dev" + user: 'postgres', + password: 'password', + database: 'default_dev' }, meta: { - tn: "nc_evolutions", - dbAlias: "primary" + tn: 'nc_evolutions', + dbAlias: 'primary' } } ] @@ -24,23 +24,23 @@ module.exports = { test: { db: [ { - client: "pg", + client: 'pg', connection: { - host: DOCKER_DB_HOST || "localhost", + host: DOCKER_DB_HOST || 'localhost', port: DOCKER_DB_PORT || 5432, - user: "postgres", - password: "password", - database: "default_test" + user: 'postgres', + password: 'password', + database: 'default_test' }, meta: { - tn: "nc_evolutions", - dbAlias: "primary" + tn: 'nc_evolutions', + dbAlias: 'primary' } } ] } }, - workingEnv: "_noco", + workingEnv: '_noco', meta: { version: '0.5', seedsFolder: 'seeds', diff --git a/packages/nocodb/src/lib/migrator/SqlMigrator/lib/templates/sqlite.template.ts b/packages/nocodb/src/lib/migrator/SqlMigrator/lib/templates/sqlite.template.ts index 290f3ff2c0..526e983b62 100644 --- a/packages/nocodb/src/lib/migrator/SqlMigrator/lib/templates/sqlite.template.ts +++ b/packages/nocodb/src/lib/migrator/SqlMigrator/lib/templates/sqlite.template.ts @@ -1,26 +1,26 @@ -import path from "path"; +import path from 'path'; -const {DOCKER_DB_FILE} = process.env; +const { DOCKER_DB_FILE } = process.env; module.exports = { - title: "default", + title: 'default', envs: { _noco: { db: [ { - client: "sqlite3", + client: 'sqlite3', connection: { - client: "sqlite3", + client: 'sqlite3', connection: { filename: DOCKER_DB_FILE || - `${path.join(process.cwd(), "xmigrator", "default_dev.db")}` + `${path.join(process.cwd(), 'xmigrator', 'default_dev.db')}` }, useNullAsDefault: true }, meta: { - tn: "nc_evolutions", - dbAlias: "primary" + tn: 'nc_evolutions', + dbAlias: 'primary' } } ] @@ -29,25 +29,25 @@ module.exports = { api: {}, db: [ { - client: "sqlite3", + client: 'sqlite3', connection: { - client: "sqlite3", + client: 'sqlite3', connection: { filename: DOCKER_DB_FILE || - `${path.join(process.cwd(), "xmigrator", "default_test.db")}` + `${path.join(process.cwd(), 'xmigrator', 'default_test.db')}` }, useNullAsDefault: true }, meta: { - tn: "nc_evolutions", - dbAlias: "primary" + tn: 'nc_evolutions', + dbAlias: 'primary' } } ] } }, - workingEnv: "_noco", + workingEnv: '_noco', meta: { version: '0.5', seedsFolder: 'seeds', diff --git a/packages/nocodb/src/lib/migrator/util/Debug.ts b/packages/nocodb/src/lib/migrator/util/Debug.ts index fa819eaa3e..dadd6b0dd5 100644 --- a/packages/nocodb/src/lib/migrator/util/Debug.ts +++ b/packages/nocodb/src/lib/migrator/util/Debug.ts @@ -1,17 +1,16 @@ -import boxen from "boxen"; -import debug from "debug"; +import boxen from 'boxen'; +import debug from 'debug'; -import("colors"); -import DebugMgr from "./DebugMgr"; +import('colors'); +import DebugMgr from './DebugMgr'; export default class Debug { - - public namespace:any; - public api:any; - public warn:any; - public info:any; - public error:any; - public debug:any; + public namespace: any; + public api: any; + public warn: any; + public info: any; + public error: any; + public debug: any; constructor(namespace) { this.namespace = namespace; @@ -25,25 +24,23 @@ export default class Debug { } ppException(e, func = null) { - let log = ""; - log += ` EXCEPTION OCCURED!! in ${ - this.namespace.red.bold - } @ ${func}`; - log += "\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -\n" + let log = ''; + log += ` EXCEPTION OCCURED!! in ${this.namespace.red.bold} @ ${func}`; + log += '\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -\n' .red.bold; log += `MESSAGE:\n`.yellow.bold; log += `${e.message}\n`.yellow.bold; - log += "\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -\n" + log += '\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -\n' .red.bold; log += `CODE:\n`.yellow.bold; log += `${e.code}\n`.yellow.bold; - log += "\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -\n" + log += '\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -\n' .red.bold; log += `STACK:\n`.yellow.bold; log += `${e.stack}\n`.yellow.bold; - log += "\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -\n" + log += '\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -\n' .red.bold; - console.log(boxen(log, { padding: 1, borderStyle: "double" })); + console.log(boxen(log, { padding: 1, borderStyle: 'double' })); console.log(e); return log; } diff --git a/packages/nocodb/src/lib/migrator/util/DebugMgr.ts b/packages/nocodb/src/lib/migrator/util/DebugMgr.ts index 3a81a37a9c..7a1f4d7865 100644 --- a/packages/nocodb/src/lib/migrator/util/DebugMgr.ts +++ b/packages/nocodb/src/lib/migrator/util/DebugMgr.ts @@ -1,13 +1,13 @@ -import debug from "debug"; +import debug from 'debug'; const namespaces = {}; const levels = { - api: "A", - info: "I", - error: "E", - warn: "W", - debug: "D" + api: 'A', + info: 'I', + error: 'E', + warn: 'W', + debug: 'D' }; export default class DebugMgr { static _create(namespace) { @@ -31,23 +31,23 @@ export default class DebugMgr { namespaces[namespace] = {}; namespaces[namespace][`${namespace}_A`] = { - level: "api", + level: 'api', enabled: debug.enabled(`${namespace}_A`) }; namespaces[namespace][`${namespace}_W`] = { - level: "warn", + level: 'warn', enabled: debug.enabled(`${namespace}_W`) }; namespaces[namespace][`${namespace}_I`] = { - level: "info", + level: 'info', enabled: debug.enabled(`${namespace}_I`) }; namespaces[namespace][`${namespace}_E`] = { - level: "error", + level: 'error', enabled: debug.enabled(`${namespace}_E`) }; namespaces[namespace][`${namespace}_D`] = { - level: "debug", + level: 'debug', enabled: debug.enabled(`${namespace}_D`) }; } @@ -77,7 +77,7 @@ export default class DebugMgr { static disable(namespace, level) { const toBeRemoved = `${namespace}_${levels[level]}`; let list = `${debug.disable()}`; - list = list.replace(toBeRemoved, ""); + list = list.replace(toBeRemoved, ''); debug.enable(list); this.refreshNamespace(namespace); } diff --git a/packages/nocodb/src/lib/migrator/util/FileCollection.ts b/packages/nocodb/src/lib/migrator/util/FileCollection.ts index 4ae0308e05..316ebccb7e 100644 --- a/packages/nocodb/src/lib/migrator/util/FileCollection.ts +++ b/packages/nocodb/src/lib/migrator/util/FileCollection.ts @@ -1,11 +1,11 @@ import fs from 'fs'; -import { promisify } from "util"; +import { promisify } from 'util'; import jsonfile from 'jsonfile'; export default class FileCollection { -public args; -public path; + public args; + public path; constructor(args) { this.args = args; this.path = args.path; @@ -18,7 +18,7 @@ public path; */ const exists = await promisify(fs.exists)(this.args.path); - if(!exists) { + if (!exists) { await promisify(jsonfile.writeFile)(this.args.path, [], { spaces: 2 }); @@ -29,17 +29,13 @@ public path; return await promisify(jsonfile.readFile)(this.args.path); } - async write(args) { await promisify(jsonfile.writeFile)(this.args.path, args.data, { spaces: 2 }); } - - } - /** * @copyright Copyright (c) 2021, Xgene Cloud Ltd * diff --git a/packages/nocodb/src/lib/migrator/util/Result.ts b/packages/nocodb/src/lib/migrator/util/Result.ts index 63794171ad..bdd8d6aa3f 100644 --- a/packages/nocodb/src/lib/migrator/util/Result.ts +++ b/packages/nocodb/src/lib/migrator/util/Result.ts @@ -1,8 +1,8 @@ export default class Result { - public code:any; - public message:any; - public data:any; - public object:any; + public code: any; + public message: any; + public data: any; + public object: any; constructor(code = 0, message = '', data = {}) { this.code = code; this.message = message; diff --git a/packages/nocodb/src/lib/migrator/util/emit.ts b/packages/nocodb/src/lib/migrator/util/emit.ts index b86adf97e0..431ddb257e 100644 --- a/packages/nocodb/src/lib/migrator/util/emit.ts +++ b/packages/nocodb/src/lib/migrator/util/emit.ts @@ -1,9 +1,9 @@ -import Emittery from "emittery"; +import Emittery from 'emittery'; let emitSingleton = null; export default class Emit { - public evt:any; + public evt: any; constructor() { if (emitSingleton) return emitSingleton; diff --git a/packages/nocodb/src/lib/migrator/util/file.help.ts b/packages/nocodb/src/lib/migrator/util/file.help.ts index 84a61f9ed5..7698bd21f8 100644 --- a/packages/nocodb/src/lib/migrator/util/file.help.ts +++ b/packages/nocodb/src/lib/migrator/util/file.help.ts @@ -1,30 +1,19 @@ import dayjs from 'dayjs'; - -const getUniqFilenamePrefix = function () { - - return dayjs().format('YYYYMMDD_HHmmssSSS') - +const getUniqFilenamePrefix = function() { + return dayjs().format('YYYYMMDD_HHmmssSSS'); }; -const getFilenameForUp = function (prefix) { - - return prefix + '.up.sql' - +const getFilenameForUp = function(prefix) { + return prefix + '.up.sql'; }; -const getFilenameForDown = function (prefix) { - - return prefix + '.down.sql' - -} +const getFilenameForDown = function(prefix) { + return prefix + '.down.sql'; +}; -export { - getUniqFilenamePrefix, - getFilenameForUp, - getFilenameForDown -} -;/** +export { getUniqFilenamePrefix, getFilenameForUp, getFilenameForDown }; +/** * @copyright Copyright (c) 2021, Xgene Cloud Ltd * * @author Naveen MR diff --git a/packages/nocodb/src/lib/noco/NcProjectBuilder.ts b/packages/nocodb/src/lib/noco/NcProjectBuilder.ts index 062f7eb319..2c337b437b 100644 --- a/packages/nocodb/src/lib/noco/NcProjectBuilder.ts +++ b/packages/nocodb/src/lib/noco/NcProjectBuilder.ts @@ -1,21 +1,20 @@ -import fs from "fs"; -import path from "path"; +import fs from 'fs'; +import path from 'path'; -import axios from "axios"; -import {Router} from "express"; -import {SqlClientFactory, Tele} from 'nc-help'; +import axios from 'axios'; +import { Router } from 'express'; +import { SqlClientFactory, Tele } from 'nc-help'; -import {NcConfig} from "../../interface/config"; +import { NcConfig } from '../../interface/config'; import Migrator from '../migrator/SqlMigrator/lib/KnexMigrator'; -import Noco from "./Noco"; -import {GqlApiBuilder} from "./gql/GqlApiBuilder"; -import {XCEeError} from "./meta/NcMetaMgr"; -import {RestApiBuilder} from "./rest/RestApiBuilder"; -import NcConnectionMgr from "./common/NcConnectionMgr"; +import Noco from './Noco'; +import { GqlApiBuilder } from './gql/GqlApiBuilder'; +import { XCEeError } from './meta/NcMetaMgr'; +import { RestApiBuilder } from './rest/RestApiBuilder'; +import NcConnectionMgr from './common/NcConnectionMgr'; export default class NcProjectBuilder { - public readonly id: string; public readonly title: string; public readonly description: string; @@ -38,16 +37,13 @@ export default class NcProjectBuilder { this.id = project.id; this.title = project.title; this.description = project.description; - this._config = {...this.appConfig, ...JSON.parse(project.config)}; + this._config = { ...this.appConfig, ...JSON.parse(project.config) }; this.router = Router(); } } - public async init(isFirstTime?: boolean) { - try { - await this.addAuthHookToMiddleware(); this.startTime = Date.now(); @@ -59,13 +55,16 @@ export default class NcProjectBuilder { /* Create REST APIs / GraphQL Resolvers */ for (const meta of this.apiBuilders) { - let routeInfo; if (meta instanceof RestApiBuilder) { - console.log(`Creating REST APIs ${meta.getDbType()} - > ${meta.getDbName()}`); + console.log( + `Creating REST APIs ${meta.getDbType()} - > ${meta.getDbName()}` + ); routeInfo = await (meta as RestApiBuilder).init(); } else if (meta instanceof GqlApiBuilder) { - console.log(`Creating GraphQL APIs ${meta.getDbType()} - > ${meta.getDbName()}`); + console.log( + `Creating GraphQL APIs ${meta.getDbType()} - > ${meta.getDbName()}` + ); routeInfo = await (meta as GqlApiBuilder).init(); } allRoutesInfo.push(routeInfo); @@ -74,32 +73,44 @@ export default class NcProjectBuilder { this.app.projectRouter.use(`/nc/${this.id}`, this.router); await this.app.ncMeta.projectStatusUpdate(this.title, 'started'); - } catch (e) { console.log(e); await this.app.ncMeta.projectStatusUpdate(this.title, 'stopped'); } } - public async handleRunTimeChanges(data: any): Promise { const curBuilder = this.apiBuilders.find(builder => { - return (data.req?.dbAlias || data.req?.args?.dbAlias) === builder.getDbAlias(); + return ( + (data.req?.dbAlias || data.req?.args?.dbAlias) === builder.getDbAlias() + ); }); switch (data?.req?.api) { - case 'xcAuthHookSet': - this.authHook = await this.app.ncMeta.metaGet(this.id, 'db', 'nc_hooks', { - type: 'AUTH_MIDDLEWARE' - }); + this.authHook = await this.app.ncMeta.metaGet( + this.id, + 'db', + 'nc_hooks', + { + type: 'AUTH_MIDDLEWARE' + } + ); break; case 'xcM2MRelationCreate': - await curBuilder.onManyToManyRelationCreate(data.req.args.parentTable, data.req.args.childTable, data.req.args); + await curBuilder.onManyToManyRelationCreate( + data.req.args.parentTable, + data.req.args.childTable, + data.req.args + ); break; case 'relationCreate': - await curBuilder.onRelationCreate(data.req.args.parentTable, data.req.args.childTable, data.req.args); + await curBuilder.onRelationCreate( + data.req.args.parentTable, + data.req.args.childTable, + data.req.args + ); this.app.ncMeta.audit(this.id, curBuilder.getDbAlias(), 'nc_audit', { op_type: 'RELATION', @@ -108,11 +119,17 @@ export default class NcProjectBuilder { description: `created relation between tables ${data.req.args.childTable} and ${data.req.args.parentTable} `, ip: data.ctx.req.clientIp }); - console.log(`Added new relation between : ${data.req.args.parentTable} ==> ${data.req.args.childTable}`) + console.log( + `Added new relation between : ${data.req.args.parentTable} ==> ${data.req.args.childTable}` + ); break; case 'relationDelete': - await curBuilder.onRelationDelete(data.req.args.parentTable, data.req.args.childTable, data.req.args); + await curBuilder.onRelationDelete( + data.req.args.parentTable, + data.req.args.childTable, + data.req.args + ); this.app.ncMeta.audit(this.id, curBuilder.getDbAlias(), 'nc_audit', { op_type: 'RELATION', op_sub_type: 'DELETED', @@ -120,29 +137,48 @@ export default class NcProjectBuilder { description: `deleted relation between tables ${data.req.args.childTable} and ${data.req.args.parentTable} `, ip: data.ctx.req.clientIp }); - console.log(`Deleted relation between : ${data.req.args.parentTable} ==> ${data.req.args.childTable}`) + console.log( + `Deleted relation between : ${data.req.args.parentTable} ==> ${data.req.args.childTable}` + ); break; - case 'xcVirtualRelationCreate': - await curBuilder.onVirtualRelationCreate(data.req.args.parentTable, data.req.args.childTable); - await curBuilder.onRelationCreate(data.req.args.parentTable, data.req.args.childTable, { - ...data.req.args, - virtual: true - }); - console.log(`Added new relation between : ${data.req.args.parentTable} ==> ${data.req.args.childTable}`) + await curBuilder.onVirtualRelationCreate( + data.req.args.parentTable, + data.req.args.childTable + ); + await curBuilder.onRelationCreate( + data.req.args.parentTable, + data.req.args.childTable, + { + ...data.req.args, + virtual: true + } + ); + console.log( + `Added new relation between : ${data.req.args.parentTable} ==> ${data.req.args.childTable}` + ); break; case 'xcVirtualRelationDelete': - await curBuilder.onRelationDelete(data.req.args.parentTable, data.req.args.childTable, { - ...data.req.args, - virtual: true - }); - console.log(`Added new relation between : ${data.req.args.parentTable} ==> ${data.req.args.childTable}`) + await curBuilder.onRelationDelete( + data.req.args.parentTable, + data.req.args.childTable, + { + ...data.req.args, + virtual: true + } + ); + console.log( + `Added new relation between : ${data.req.args.parentTable} ==> ${data.req.args.childTable}` + ); break; case 'xcRelationColumnDelete': if (data.req.args?.type === 'mm') { - await curBuilder.onManyToManyRelationDelete(data.req.args.parentTable, data.req.args.childTable) + await curBuilder.onManyToManyRelationDelete( + data.req.args.parentTable, + data.req.args.childTable + ); } break; @@ -154,7 +190,6 @@ export default class NcProjectBuilder { await curBuilder.loadFormViews(); break; - case 'tableCreate': await curBuilder.onTableCreate(data.req.args.tn, data.req.args); @@ -164,8 +199,8 @@ export default class NcProjectBuilder { user: data.user.email, description: `created table ${data.req.args.tn} with alias ${data.req.args._tn} `, ip: data.ctx.req.clientIp - }) - console.log(`Added new routes for table : ${data.req.args.tn}`) + }); + console.log(`Added new routes for table : ${data.req.args.tn}`); break; case 'viewCreate': @@ -174,9 +209,10 @@ export default class NcProjectBuilder { op_type: 'VIEW', op_sub_type: 'CREATED', user: data.user.email, - description: `created view ${data.req.args.view_name} `, ip: data.ctx.req.clientIp - }) - console.log(`Added new routes for table : ${data.req.args.tn}`) + description: `created view ${data.req.args.view_name} `, + ip: data.ctx.req.clientIp + }); + console.log(`Added new routes for table : ${data.req.args.tn}`); break; case 'viewUpdate': @@ -185,9 +221,10 @@ export default class NcProjectBuilder { op_type: 'VIEW', op_sub_type: 'UPDATED', user: data.user.email, - description: `updated view ${data.req.args.view_name} `, ip: data.ctx.req.clientIp - }) - console.log(`Added new routes for table : ${data.req.args.tn}`) + description: `updated view ${data.req.args.view_name} `, + ip: data.ctx.req.clientIp + }); + console.log(`Added new routes for table : ${data.req.args.tn}`); break; case 'tableDelete': @@ -196,9 +233,10 @@ export default class NcProjectBuilder { op_type: 'TABLE', op_sub_type: 'DELETED', user: data.user.email, - description: `deleted table ${data.req.args.tn} `, ip: data.ctx.req.clientIp - }) - console.log(`Deleted routes for table : ${data.req.args.tn}`) + description: `deleted table ${data.req.args.tn} `, + ip: data.ctx.req.clientIp + }); + console.log(`Deleted routes for table : ${data.req.args.tn}`); break; case 'tableRename': @@ -210,50 +248,52 @@ export default class NcProjectBuilder { user: data.user.email, description: `renamed table ${data.req.args.tn_old} to ${data.req.args.tn} `, ip: data.ctx.req.clientIp - }) - console.log(`Updated routes for table : ${data.req.args.tn}`) + }); + console.log(`Updated routes for table : ${data.req.args.tn}`); break; - case 'xcRoutesHandlerUpdate': case 'xcResolverHandlerUpdate': case 'xcRpcHandlerUpdate': // todo: implement separate function await curBuilder.onHandlerCodeUpdate(data.req.args.tn); - console.log(`Updated routes handler for table : ${data.req.tn}`) + console.log(`Updated routes handler for table : ${data.req.tn}`); break; - case 'xcRoutesMiddlewareUpdate': case 'xcResolverMiddlewareUpdate': // todo: implement separate function await curBuilder.onMiddlewareCodeUpdate(data.req.args.tn); - console.log(`Updated routes handler for table : ${data.req.args.tn}`) + console.log(`Updated routes handler for table : ${data.req.args.tn}`); break; case 'xcModelSet': await curBuilder.onMetaUpdate(data.req.args.tn); - console.log(`Updated validations for table : ${data.req.args.tn}`) + console.log(`Updated validations for table : ${data.req.args.tn}`); break; case 'xcUpdateVirtualKeyAlias': await curBuilder.onVirtualColumnAliasUpdate(data.req.args.tn); - console.log(`Updated validations for table : ${data.req.args.tn}`) + console.log(`Updated validations for table : ${data.req.args.tn}`); break; case 'xcModelSchemaSet': - await curBuilder.onGqlSchemaUpdate(data.req.args.tn, data.req.args.schema); - console.log(`Updated validations for table : ${data.req.args.tn}`) + await curBuilder.onGqlSchemaUpdate( + data.req.args.tn, + data.req.args.schema + ); + console.log(`Updated validations for table : ${data.req.args.tn}`); break; - case 'tableXcHooksSet': await curBuilder.onHooksUpdate(data.req.args.tn); - console.log(`Updated validations for table : ${data.req.args.tn}`) + console.log(`Updated validations for table : ${data.req.args.tn}`); break; case 'xcModelSwaggerDocSet': - await (curBuilder as RestApiBuilder).onSwaggerDocUpdate(data.req.args.tn); - console.log(`Updated validations for table : ${data.req.args.tn}`) + await (curBuilder as RestApiBuilder).onSwaggerDocUpdate( + data.req.args.tn + ); + console.log(`Updated validations for table : ${data.req.args.tn}`); break; case 'tableUpdate': @@ -264,40 +304,40 @@ export default class NcProjectBuilder { user: data.user.email, description: `updated table ${data.req.args.tn} with alias ${data.req.args._tn} `, ip: data.ctx.req.clientIp - }) - console.log(`Updated validations for table : ${data.req.args.tn}`) + }); + console.log(`Updated validations for table : ${data.req.args.tn}`); break; case 'procedureCreate': - await curBuilder.onProcedureCreate(data.req.args.procedure_name) + await curBuilder.onProcedureCreate(data.req.args.procedure_name); break; case 'functionUpdate': - await curBuilder.onFunctionDelete(data.req.args.function_name) - await curBuilder.onFunctionCreate(data.req.args.function_name) + await curBuilder.onFunctionDelete(data.req.args.function_name); + await curBuilder.onFunctionCreate(data.req.args.function_name); break; case 'procedureUpdate': - await curBuilder.onProcedureDelete(data.req.args.procedure_name) - await curBuilder.onProcedureCreate(data.req.args.procedure_name) + await curBuilder.onProcedureDelete(data.req.args.procedure_name); + await curBuilder.onProcedureCreate(data.req.args.procedure_name); break; case 'procedureDelete': - await curBuilder.onProcedureDelete(data.req.args.procedure_name) + await curBuilder.onProcedureDelete(data.req.args.procedure_name); break; case 'functionCreate': - await curBuilder.onFunctionCreate(data.req.args.function_name) + await curBuilder.onFunctionCreate(data.req.args.function_name); break; case 'functionDelete': - await curBuilder.onFunctionDelete(data.req.args.function_name) + await curBuilder.onFunctionDelete(data.req.args.function_name); break; case 'xcRoutesPolicyUpdate': case 'xcResolverPolicyUpdate': await curBuilder.onPolicyUpdate(data.req.args.tn); - console.log(`Updated validations for table : ${data.req.args.tn}`) + console.log(`Updated validations for table : ${data.req.args.tn}`); break; case 'xcModelsEnable': @@ -325,18 +365,17 @@ export default class NcProjectBuilder { break; case 'xcCronSave': - await curBuilder.restartCron(data.req.args) + await curBuilder.restartCron(data.req.args); break; case 'cronDelete': - await curBuilder.removeCron(data.req.args) + await curBuilder.removeCron(data.req.args); break; - // todo: optimize if (Array.isArray(data.req.args.tableNames)) { for (const procedure of data.req.args.tableNames) { - await curBuilder.onProcedureCreate(procedure) + await curBuilder.onProcedureCreate(procedure); } } case 'tableMetaCreate': @@ -355,13 +394,14 @@ export default class NcProjectBuilder { XCEeError.throw(); break; case 'viewDelete': - await curBuilder.onViewDelete(data.req.args.view_name) + await curBuilder.onViewDelete(data.req.args.view_name); this.app.ncMeta.audit(this.id, curBuilder.getDbAlias(), 'nc_audit', { op_type: 'VIEW', op_sub_type: 'DELETED', user: data.user.email, - description: `deleted view ${data.req.args.view_name} `, ip: data.ctx.req.clientIp - }) + description: `deleted view ${data.req.args.view_name} `, + ip: data.ctx.req.clientIp + }); break; case 'tableMetaRecreate': @@ -391,28 +431,27 @@ export default class NcProjectBuilder { break; case 'procedureMetaDelete': - await curBuilder.onProcedureDelete(data.req.args.procedure_name) + await curBuilder.onProcedureDelete(data.req.args.procedure_name); break; case 'procedureMetaRecreate': - await curBuilder.onProcedureDelete(data.req.args.tn) - await curBuilder.onProcedureCreate(data.req.args.tn) + await curBuilder.onProcedureDelete(data.req.args.tn); + await curBuilder.onProcedureCreate(data.req.args.tn); break; case 'functionMetaCreate': // todo: optimize if (Array.isArray(data.req.args.tableNames)) { for (const functionName of data.req.args.tableNames) { - await curBuilder.onFunctionCreate(functionName) + await curBuilder.onFunctionCreate(functionName); } } break; case 'functionMetaDelete': - await curBuilder.onFunctionDelete(data.req.args.procedure_name) + await curBuilder.onFunctionDelete(data.req.args.procedure_name); break; - case 'projectStop': this.router.stack.splice(0, this.router.stack.length); this.apiBuilders.splice(0, this.apiBuilders.length); @@ -422,8 +461,9 @@ export default class NcProjectBuilder { op_type: 'PROJECT', op_sub_type: 'STOPPED', user: data.user.email, - description: `stopped project ${this.title}(${this.id}) `, ip: data?.ctx?.req?.clientIp - }) + description: `stopped project ${this.title}(${this.id}) `, + ip: data?.ctx?.req?.clientIp + }); break; case 'projectStart': @@ -432,16 +472,21 @@ export default class NcProjectBuilder { op_type: 'PROJECT', op_sub_type: 'STARTED', user: data.user.email, - description: `started project ${this.title}(${this.id}) `, ip: data?.ctx?.req?.clientIp - }) + description: `started project ${this.title}(${this.id}) `, + ip: data?.ctx?.req?.clientIp + }); break; case 'projectDelete': this.router.stack.splice(0, this.router.stack.length); this.apiBuilders.splice(0, this.apiBuilders.length); await this.app.ncMeta.projectDeleteById(this.id); - await this.app.ncMeta.knex('nc_projects_users').where({project_id: this.id}).del(); - for (const db of (this.config?.envs?.[this.appConfig?.workingEnv]?.db || [])) { + await this.app.ncMeta + .knex('nc_projects_users') + .where({ project_id: this.id }) + .del(); + for (const db of this.config?.envs?.[this.appConfig?.workingEnv]?.db || + []) { const dbAlias = db?.meta?.dbAlias; const apiType = db?.meta?.api?.type; await this.app.ncMeta.metaReset(this.id, dbAlias, apiType); @@ -451,8 +496,9 @@ export default class NcProjectBuilder { op_type: 'PROJECT', op_sub_type: 'DELETED', user: data.user.email, - description: `deleted project ${this.title}(${this.id}) `, ip: data?.ctx?.req?.clientIp - }) + description: `deleted project ${this.title}(${this.id}) `, + ip: data?.ctx?.req?.clientIp + }); break; case 'xcMetaTablesImportLocalFsToDb': @@ -463,18 +509,17 @@ export default class NcProjectBuilder { op_type: 'PROJECT', op_sub_type: 'RESTARTED', user: data.user.email, - description: `restarted project ${this.title}(${this.id}) `, ip: data?.ctx?.req?.clientIp - }) + description: `restarted project ${this.title}(${this.id}) `, + ip: data?.ctx?.req?.clientIp + }); break; default: console.log('DB OPS', data.req.api); } - // export metadata to filesystem after meta changes switch (data?.req?.api) { - case 'procedureCreate': case 'functionUpdate': case 'procedureUpdate': @@ -509,23 +554,20 @@ export default class NcProjectBuilder { } break; } - } - protected async _createApiBuilder() { - this.apiBuilders.splice(0, this.apiBuilders.length); let i = 0; const connectionConfigs = []; /* for each db create an api builder */ - for (const db of (this.config?.envs?.[this.appConfig?.workingEnv]?.db || [])) { - + for (const db of this.config?.envs?.[this.appConfig?.workingEnv]?.db || + []) { let Builder; switch (db.meta.api.type) { - case "graphql": + case 'graphql': Builder = GqlApiBuilder; break; @@ -535,7 +577,6 @@ export default class NcProjectBuilder { } if ((db?.connection as any)?.database) { - const connectionConfig = { ...db, meta: { @@ -547,16 +588,22 @@ export default class NcProjectBuilder { } }; - - this.apiBuilders.push(new Builder(this.app, this, this.config, connectionConfig, this.app.ncMeta)); + this.apiBuilders.push( + new Builder( + this.app, + this, + this.config, + connectionConfig, + this.app.ncMeta + ) + ); connectionConfigs.push(connectionConfig); i++; } else if (db.meta?.allSchemas) { - /* get all schemas and create APIs for all of them */ const sqlClient = SqlClientFactory.create({ ...db, - connection: {...db.connection, database: undefined} + connection: { ...db.connection, database: undefined } }); // @ts-ignore @@ -564,7 +611,7 @@ export default class NcProjectBuilder { for (const schema of schemaList) { const connectionConfig = { ...db, - connection: {...db.connection, database: schema.schema_name}, + connection: { ...db.connection, database: schema.schema_name }, meta: { ...db.meta, dbAlias: i ? db.meta.dbAlias + i : db.meta.dbAlias, @@ -575,54 +622,62 @@ export default class NcProjectBuilder { } }; - this.apiBuilders.push(new Builder(this.app, this, this.config, connectionConfig, this.app.ncMeta)); + this.apiBuilders.push( + new Builder( + this.app, + this, + this.config, + connectionConfig, + this.app.ncMeta + ) + ); connectionConfigs.push(connectionConfig); i++; } sqlClient.knex.destroy(); - } } if (this.config?.envs?.[this.appConfig.workingEnv]?.db) { - this.config.envs[this.appConfig.workingEnv].db.splice(0, this.config.envs[this.appConfig.workingEnv].db.length, ...connectionConfigs); + this.config.envs[this.appConfig.workingEnv].db.splice( + 0, + this.config.envs[this.appConfig.workingEnv].db.length, + ...connectionConfigs + ); } } - protected genVer(i): string { const l = 'vwxyzabcdefghijklmnopqrstu'; - return i - .toString(26) - .split('') - .map(v => l[parseInt(v, 26)]) - .join('') + '1'; + return ( + i + .toString(26) + .split('') + .map(v => l[parseInt(v, 26)]) + .join('') + '1' + ); } - protected async syncMigration(): Promise { - - if (this.appConfig?.toolDir + if ( + this.appConfig?.toolDir // && !('NC_MIGRATIONS_DISABLED' in process.env) ) { - - const dbs = this.config?.envs?.[this.appConfig.workingEnv]?.db + const dbs = this.config?.envs?.[this.appConfig.workingEnv]?.db; if (!dbs || !dbs.length) { return; } for (const connectionConfig of dbs) { - try { - const sqlClient = NcConnectionMgr.getSqlClient({ dbAlias: connectionConfig?.mets?.dbAlias, env: this.config.env, config: this.config, projectId: this.id - }) + }); /* create migrator */ const migrator = new Migrator({ project_id: this.id, @@ -631,7 +686,13 @@ export default class NcProjectBuilder { }); /* if migrator folder doesn't exist for project - call migratior init */ - const migrationFolder = path.join(this.config.toolDir, 'nc', this.id, connectionConfig.meta.dbAlias, 'migrations'); + const migrationFolder = path.join( + this.config.toolDir, + 'nc', + this.id, + connectionConfig.meta.dbAlias, + 'migrations' + ); if (!fs.existsSync(migrationFolder)) { await migrator.init({ folder: this.config?.toolDir, @@ -656,7 +717,6 @@ export default class NcProjectBuilder { sqlContentMigrate: 1, sqlClient }); - } catch (e) { console.log(e); // throw e; @@ -666,44 +726,47 @@ export default class NcProjectBuilder { } } - protected static triggerGarbageCollect() { try { if (global.gc) { global.gc(); } } catch (e) { - console.log("`node --expose-gc index.js`"); + console.log('`node --expose-gc index.js`'); process.exit(); } } - protected initApiInfoRoute(): void { - this.router.get(`/projectApiInfo`, (req: any, res): any => { - // auth to admin if (this.config.auth) { if (this.config.auth.jwt) { - if (!(req.session.passport.user.roles.creator - || req.session.passport.user.roles.editor - || req.session.passport.user.roles.commenter - || req.session.passport.user.roles.viewer)) { + if ( + !( + req.session.passport.user.roles.creator || + req.session.passport.user.roles.editor || + req.session.passport.user.roles.commenter || + req.session.passport.user.roles.viewer + ) + ) { return res.status(401).json({ - msg: 'Unauthorized access : xc-auth does not have admin permission' - }) + msg: + 'Unauthorized access : xc-auth does not have admin permission' + }); } } else if (this.config.auth.masterKey) { - if (req.headers['xc-master-key'] !== this.config.auth.masterKey.secret) { + if ( + req.headers['xc-master-key'] !== this.config.auth.masterKey.secret + ) { return res.status(401).json({ - msg: 'Unauthorized access : xc-admin header missing or not matching' - }) + msg: + 'Unauthorized access : xc-admin header missing or not matching' + }); } } } - const info: any = {}; for (const builder of this.apiBuilders) { @@ -713,8 +776,8 @@ export default class NcProjectBuilder { gqlApiUrl: `/nc/${this.id}/${builder.apiPrefix}/graphql`, grpcApiUrl: ``, apiType: builder.apiType, - database: builder.getDbName(), - } + database: builder.getDbName() + }; } const result = { @@ -723,62 +786,64 @@ export default class NcProjectBuilder { list: this.apiInfInfoList, aggregated: this.aggregatedApiInfo } - } + }; res.json(result); }); } - protected async progress(info, allInfo, isFirstTime?) { - - const aggregatedInfo = allInfo.reduce((arrSum, infoObj) => [ - '', - arrSum[1] + +infoObj.tablesCount, - - arrSum[2] + (infoObj.type === 'graphql' ? 1 : 0), - arrSum[3] + +(infoObj.type === 'rest' ? 1 : 0), - - arrSum[4] + (+infoObj.apiCount || +infoObj.resolversCount || 0), - // arrSum[3] + +info.timeTaken - (Date.now() - this.startTime) / 1000 - ], - [0, 0, 0, 0, 0, 0]) - .map((v, i) => (i === 5 ? v.toFixed(1) + 's' : (i === 2 ? v.toLocaleString() : v))); - + const aggregatedInfo = allInfo + .reduce( + (arrSum, infoObj) => [ + '', + arrSum[1] + +infoObj.tablesCount, + + arrSum[2] + (infoObj.type === 'graphql' ? 1 : 0), + arrSum[3] + +(infoObj.type === 'rest' ? 1 : 0), + + arrSum[4] + (+infoObj.apiCount || +infoObj.resolversCount || 0), + // arrSum[3] + +info.timeTaken + (Date.now() - this.startTime) / 1000 + ], + [0, 0, 0, 0, 0, 0] + ) + .map((v, i) => + i === 5 ? v.toFixed(1) + 's' : i === 2 ? v.toLocaleString() : v + ); this.apiInfInfoList.push(info); this.aggregatedApiInfo = aggregatedInfo; if (isFirstTime) { Tele.emit('evt_api_created', info); } - } - protected async addAuthHookToMiddleware(): Promise { this.authHook = await this.app.ncMeta.metaGet(this.id, 'db', 'nc_hooks', { type: 'AUTH_MIDDLEWARE' }); this.router.use(async (req: any, _res, next) => { - if (this.authHook && this.authHook.url) { try { - const result = await axios.post(this.authHook.url, {}, { - headers: req.headers - }); + const result = await axios.post( + this.authHook.url, + {}, + { + headers: req.headers + } + ); req.locals = req.locals || {}; - req.locals = {user: result.data}; + req.locals = { user: result.data }; } catch (e) { - console.log(e) + console.log(e); } } next(); - }) + }); } - public get prefix(): string { return this.config?.prefix; } @@ -788,14 +853,14 @@ export default class NcProjectBuilder { } public updateConfig(config: string) { - this._config = {...this.appConfig, ...JSON.parse(config)}; + this._config = { ...this.appConfig, ...JSON.parse(config) }; } public async reInit() { this.router.stack.splice(0, this.router.stack.length); this.apiBuilders.splice(0, this.apiBuilders.length); await this.app.ncMeta.projectStatusUpdate(this.title, 'stopped'); - const dbs = this.config?.envs?.[this.appConfig.workingEnv]?.db + const dbs = this.config?.envs?.[this.appConfig.workingEnv]?.db; if (!dbs || !dbs.length) { return; @@ -806,12 +871,11 @@ export default class NcProjectBuilder { dbAlias: connectionConfig?.mets?.dbAlias, env: this.config.env, projectId: this.id - }) + }); } NcProjectBuilder.triggerGarbageCollect(); await this.init(); } - } /** diff --git a/packages/nocodb/src/lib/noco/NcProjectBuilderEE.ts b/packages/nocodb/src/lib/noco/NcProjectBuilderEE.ts index eb763f974f..011a9433a8 100644 --- a/packages/nocodb/src/lib/noco/NcProjectBuilderEE.ts +++ b/packages/nocodb/src/lib/noco/NcProjectBuilderEE.ts @@ -1,58 +1,62 @@ -import NcProjectBuilder from "./NcProjectBuilder"; +import NcProjectBuilder from './NcProjectBuilder'; export default class NcProjectBuilderEE extends NcProjectBuilder { - - public async handleRunTimeChanges(data: any): Promise { const curBuilder = this.apiBuilders.find(builder => { - return (data.req?.dbAlias || data.req?.args?.dbAlias) === builder.getDbAlias(); + return ( + (data.req?.dbAlias || data.req?.args?.dbAlias) === builder.getDbAlias() + ); }); switch (data?.req?.api) { - - case 'tableMetaRecreate': - await curBuilder.onTableDelete(data.req.args.tn) - await curBuilder.onTableCreate(data.req.args.tn, {}) + await curBuilder.onTableDelete(data.req.args.tn); + await curBuilder.onTableCreate(data.req.args.tn, {}); break; case 'viewMetaRecreate': - await curBuilder.onViewDelete(data.req.args.tn) - await curBuilder.onViewCreate(data.req.args.tn, {}) + await curBuilder.onViewDelete(data.req.args.tn); + await curBuilder.onViewCreate(data.req.args.tn, {}); break; case 'procedureMetaCreate': // todo: optimize if (Array.isArray(data.req.args.tableNames)) { for (const procedure of data.req.args.tableNames) { - await curBuilder.onProcedureCreate(procedure) + await curBuilder.onProcedureCreate(procedure); } } break; case 'functionMetaRecreate': - await curBuilder.onFunctionDelete(data.req.args.tn) - await curBuilder.onFunctionCreate(data.req.args.tn) + await curBuilder.onFunctionDelete(data.req.args.tn); + await curBuilder.onFunctionCreate(data.req.args.tn); break; case 'tableMetaCreate': // await curBuilder.onTableCreate(data.req.args.tn) - await curBuilder.xcTablesPopulate({tableNames: data.req.args.tableNames.map(tn => ({tn})), type:'table'}); + await curBuilder.xcTablesPopulate({ + tableNames: data.req.args.tableNames.map(tn => ({ tn })), + type: 'table' + }); break; case 'viewMetaCreate': // await curBuilder.onTableCreate(data.req.args.tn) - await curBuilder.xcTablesPopulate({tableNames: data.req.args.viewNames, type: 'view'}); + await curBuilder.xcTablesPopulate({ + tableNames: data.req.args.viewNames, + type: 'view' + }); break; case 'tableMetaDelete': for (const table of data.req.args.tableNames) { - await curBuilder.onTableDelete(table) + await curBuilder.onTableDelete(table); } break; case 'viewMetaDelete': for (const table of data.req.args.viewNames) { - await curBuilder.onViewDelete(table) + await curBuilder.onViewDelete(table); } break; case 'xcRelationsSet': @@ -60,11 +64,9 @@ export default class NcProjectBuilderEE extends NcProjectBuilder { break; default: - return super.handleRunTimeChanges(data) + return super.handleRunTimeChanges(data); } } - - } /** diff --git a/packages/nocodb/src/lib/noco/Noco.ts b/packages/nocodb/src/lib/noco/Noco.ts index ecaabb49d8..a0d0b93dad 100644 --- a/packages/nocodb/src/lib/noco/Noco.ts +++ b/packages/nocodb/src/lib/noco/Noco.ts @@ -1,47 +1,48 @@ /* eslint-disable @typescript-eslint/ban-types */ -import fs from "fs"; +import fs from 'fs'; import path from 'path'; import * as Sentry from '@sentry/node'; -import bodyParser from "body-parser"; +import bodyParser from 'body-parser'; import clear from 'clear'; import cookieParser from 'cookie-parser'; import debug from 'debug'; -import * as express from 'express' -import {Router} from "express"; -import importFresh from "import-fresh"; -import morgan from "morgan"; -import {Tele} from "nc-help"; -import NcToolGui from "nc-lib-gui"; +import * as express from 'express'; +import { Router } from 'express'; +import importFresh from 'import-fresh'; +import morgan from 'morgan'; +import { Tele } from 'nc-help'; +import NcToolGui from 'nc-lib-gui'; import requestIp from 'request-ip'; -import {v4 as uuidv4} from 'uuid'; +import { v4 as uuidv4 } from 'uuid'; -import {NcConfig} from "../../interface/config"; +import { NcConfig } from '../../interface/config'; import Migrator from '../migrator/SqlMigrator/lib/KnexMigrator'; -import NcConfigFactory from "../utils/NcConfigFactory"; - -import NcProjectBuilderCE from "./NcProjectBuilder"; -import NcProjectBuilderEE from "./NcProjectBuilderEE"; -import {GqlApiBuilder} from "./gql/GqlApiBuilder"; -import NcMetaIO from "./meta/NcMetaIO"; -import NcMetaImplCE from "./meta/NcMetaIOImpl"; -import NcMetaImplEE from "./meta/NcMetaIOImplEE"; -import NcMetaMgrCE from "./meta/NcMetaMgr"; -import NcMetaMgrEE from "./meta/NcMetaMgrEE"; -import {RestApiBuilder} from "./rest/RestApiBuilder"; -import RestAuthCtrlCE from "./rest/RestAuthCtrl"; -import RestAuthCtrlEE from "./rest/RestAuthCtrlEE"; +import NcConfigFactory from '../utils/NcConfigFactory'; + +import NcProjectBuilderCE from './NcProjectBuilder'; +import NcProjectBuilderEE from './NcProjectBuilderEE'; +import { GqlApiBuilder } from './gql/GqlApiBuilder'; +import NcMetaIO from './meta/NcMetaIO'; +import NcMetaImplCE from './meta/NcMetaIOImpl'; +import NcMetaImplEE from './meta/NcMetaIOImplEE'; +import NcMetaMgrCE from './meta/NcMetaMgr'; +import NcMetaMgrEE from './meta/NcMetaMgrEE'; +import { RestApiBuilder } from './rest/RestApiBuilder'; +import RestAuthCtrlCE from './rest/RestAuthCtrl'; +import RestAuthCtrlEE from './rest/RestAuthCtrlEE'; import mkdirp from 'mkdirp'; -import MetaAPILogger from "./meta/MetaAPILogger"; -import NcUpgrader from "./upgrader/NcUpgrader"; +import MetaAPILogger from './meta/MetaAPILogger'; +import NcUpgrader from './upgrader/NcUpgrader'; const log = debug('nc:app'); require('dotenv').config(); -const NcProjectBuilder = process.env.EE ? NcProjectBuilderEE : NcProjectBuilderCE; +const NcProjectBuilder = process.env.EE + ? NcProjectBuilderEE + : NcProjectBuilderCE; export default class Noco { - private static _this: Noco; public static get dashboardUrl(): string { @@ -53,15 +54,15 @@ export default class Noco { siteUrl = Noco._this?.config?.envs?.['_noco']?.publicUrl; } - return `${siteUrl}${Noco._this?.config?.dashboardPath}` + return `${siteUrl}${Noco._this?.config?.dashboardPath}`; } public static async init(args?: { - progressCallback?: Function, - registerRoutes?: Function, - registerGql?: Function, - registerContext?: Function, - afterMetaMigrationInit?: Function + progressCallback?: Function; + registerRoutes?: Function; + registerGql?: Function; + registerContext?: Function; + afterMetaMigrationInit?: Function; }): Promise { if (Noco._this) { return Noco._this.router; @@ -87,7 +88,6 @@ export default class Noco { private socketClient: any; constructor() { - process.env.PORT = process.env.PORT || '8080'; // todo: move process.env.NC_VERSION = '0011043'; @@ -99,7 +99,7 @@ export default class Noco { this.config = NcConfigFactory.make(); /******************* setup : start *******************/ - this.env = '_noco';//process.env['NODE_ENV'] || this.config.workingEnv || 'dev'; + this.env = '_noco'; //process.env['NODE_ENV'] || this.config.workingEnv || 'dev'; this.config.workingEnv = this.env; this.config.type = 'docker'; @@ -136,19 +136,17 @@ export default class Noco { // }); clear(); /******************* prints : end *******************/ - } public async init(args?: { - progressCallback?: Function, - registerRoutes?: Function, - registerGql?: Function, - registerContext?: Function, - afterMetaMigrationInit?: Function + progressCallback?: Function; + registerRoutes?: Function; + registerGql?: Function; + registerContext?: Function; + afterMetaMigrationInit?: Function; }) { - const { - progressCallback, + progressCallback // registerRoutes, // registerContext, // registerGql @@ -156,7 +154,6 @@ export default class Noco { log('Initializing app'); - // create tool directory if missing mkdirp.sync(this.config.toolDir); @@ -177,7 +174,7 @@ export default class Noco { await this.readOrGenJwtSecret(); - await NcUpgrader.upgrade({ncMeta: this.ncMeta}) + await NcUpgrader.upgrade({ ncMeta: this.ncMeta }); if (args?.afterMetaMigrationInit) { await args.afterMetaMigrationInit(); @@ -186,26 +183,30 @@ export default class Noco { /******************* Middlewares : start *******************/ this.router.use((req: any, _res, next) => { req.nc = this.requestContext; - req.ncSiteUrl = this.config?.envs?.[this.env]?.publicUrl || this.config?.publicUrl || (req.protocol + '://' + req.get('host')); + req.ncSiteUrl = + this.config?.envs?.[this.env]?.publicUrl || + this.config?.publicUrl || + req.protocol + '://' + req.get('host'); req.ncFullUrl = req.protocol + '://' + req.get('host') + req.originalUrl; next(); }); - // to get ip addresses - this.router.use(requestIp.mw()) + this.router.use(requestIp.mw()); this.router.use(cookieParser()); - this.router.use(bodyParser.json({ - limit: process.env.NC_REQUEST_BODY_SIZE || 1024 * 1024 - })); + this.router.use( + bodyParser.json({ + limit: process.env.NC_REQUEST_BODY_SIZE || 1024 * 1024 + }) + ); this.router.use(morgan('tiny')); this.router.use(express.static(path.join(__dirname, './public'))); this.router.use((req: any, _res, next) => { req.ncProjectId = req?.query?.project_id || req?.body?.project_id; next(); - }) + }); /* this.router.use(this.config.dashboardPath, (req: any, _res, next) => { req.ncProjectId = req?.body?.project_id; next(); @@ -213,7 +214,7 @@ export default class Noco { this.router.use(`/nc/:project_id/*`, (req: any, _res, next) => { req.ncProjectId = req.ncProjectId || req.params.project_id; next(); - }) + }); this.router.use(MetaAPILogger.mw); /******************* Middlewares : end *******************/ @@ -225,21 +226,25 @@ export default class Noco { this.ncToolApi.addListener(runTimeHandler); this.metaMgr.setListener(runTimeHandler); await this.metaMgr.initHandler(this.router); - this.router.use(this.config.dashboardPath, await this.ncToolApi.expressMiddleware()); - this.router.get('/', (_req, res) => res.redirect(this.config.dashboardPath)); + this.router.use( + this.config.dashboardPath, + await this.ncToolApi.expressMiddleware() + ); + this.router.get('/', (_req, res) => + res.redirect(this.config.dashboardPath) + ); this.initSentryErrorHandler(); /* catch error */ this.router.use((err, _req, res, next) => { if (err) { - return res.status(400).json({msg: err.message}); + return res.status(400).json({ msg: err.message }); } next(); }); - - Tele.emit('evt_app_started', {}) + Tele.emit('evt_app_started', {}); return this.router; } @@ -252,17 +257,14 @@ export default class Noco { private initSentry() { if (process.env.NC_SENTRY_DSN) { - Sentry.init({dsn: process.env.NC_SENTRY_DSN}); + Sentry.init({ dsn: process.env.NC_SENTRY_DSN }); -// The request handler must be the first middleware on the app + // The request handler must be the first middleware on the app this.router.use(Sentry.Handlers.requestHandler()); } } - async initServerless() { - - } - + async initServerless() {} public getBuilders(): Array { return this.apiBuilders; @@ -276,128 +278,150 @@ export default class Noco { this.requestContext = context; } - private handleRuntimeChanges(_progressCallback: Function) { - return async (data): Promise => { - switch (data?.req?.api) { - case 'projectCreateByWeb': case 'projectCreateByOneClick': - case 'projectCreateByWebWithXCDB': { - // || data?.req?.args?.project?.title || data?.req?.args?.title - const project = await this.ncMeta.projectGetById(data?.res?.id) - const builder = new NcProjectBuilder(this, this.config, project); - this.projectBuilders.push(builder) - await builder.init(true); - } + case 'projectCreateByWebWithXCDB': + { + // || data?.req?.args?.project?.title || data?.req?.args?.title + const project = await this.ncMeta.projectGetById(data?.res?.id); + const builder = new NcProjectBuilder(this, this.config, project); + this.projectBuilders.push(builder); + await builder.init(true); + } break; // create project builder for newly imported project // duplicated code - projectCreateByWeb - case 'xcMetaTablesImportZipToLocalFsAndDb': { - if (data.req?.freshImport) { - const project = await this.ncMeta.projectGetById(data?.req?.project_id) - const builder = new NcProjectBuilder(this, this.config, project); - this.projectBuilders.push(builder) - await builder.init(true); - } else { - const projectBuilder = this.projectBuilders.find(pb => pb.id == data.req?.project_id); - return projectBuilder?.handleRunTimeChanges(data); + case 'xcMetaTablesImportZipToLocalFsAndDb': + { + if (data.req?.freshImport) { + const project = await this.ncMeta.projectGetById( + data?.req?.project_id + ); + const builder = new NcProjectBuilder(this, this.config, project); + this.projectBuilders.push(builder); + await builder.init(true); + } else { + const projectBuilder = this.projectBuilders.find( + pb => pb.id == data.req?.project_id + ); + return projectBuilder?.handleRunTimeChanges(data); + } } - } break; - case 'projectUpdateByWeb': { - const projectId = data.req?.project_id; - const project = await this.ncMeta.projectGetById(data?.req?.project_id) - const projectBuilder = this.projectBuilders.find(pb => pb.id === projectId); - - projectBuilder.updateConfig(project.config) - await projectBuilder.reInit() - console.log(`Project updated: ${projectId}`) - } + case 'projectUpdateByWeb': + { + const projectId = data.req?.project_id; + const project = await this.ncMeta.projectGetById( + data?.req?.project_id + ); + const projectBuilder = this.projectBuilders.find( + pb => pb.id === projectId + ); + + projectBuilder.updateConfig(project.config); + await projectBuilder.reInit(); + console.log(`Project updated: ${projectId}`); + } break; case 'projectChangeEnv': try { - this.config = importFresh(path.join(process.cwd(), 'config.xc.json')) as NcConfig; + this.config = importFresh( + path.join(process.cwd(), 'config.xc.json') + ) as NcConfig; this.config.toolDir = this.config.toolDir || process.cwd(); this.ncMeta.setConfig(this.config); this.metaMgr.setConfig(this.config); - Object.assign(process.env, {NODE_ENV: this.env = this.config.workingEnv}); + Object.assign(process.env, { + NODE_ENV: this.env = this.config.workingEnv + }); this.router.stack.splice(0, this.router.stack.length); this.ncToolApi.destroy(); this.ncToolApi.reInitialize(this.config); // await this.init({progressCallback}); - console.log(`Loaded env : ${data.req.args.env}`) + console.log(`Loaded env : ${data.req.args.env}`); } catch (e) { console.log(e); } break; default: { - const projectBuilder = this.projectBuilders.find(pb => pb.id == data.req?.project_id); + const projectBuilder = this.projectBuilders.find( + pb => pb.id == data.req?.project_id + ); return projectBuilder?.handleRunTimeChanges(data); } } - }; } - private async initProjectBuilders() { - const RestAuthCtrl = process.env.EE ? RestAuthCtrlEE : RestAuthCtrlCE; this.projectBuilders.splice(0, this.projectBuilders.length); - await new RestAuthCtrl(this as any, + await new RestAuthCtrl( + this as any, this.ncMeta?.knex, this.config?.meta?.db, - this.config, this.ncMeta).init(); + this.config, + this.ncMeta + ).init(); this.router.use(this.projectRouter); const projects = await this.ncMeta.projectList(); for (const project of projects) { const projectBuilder = new NcProjectBuilder(this, this.config, project); - this.projectBuilders.push(projectBuilder) + this.projectBuilders.push(projectBuilder); } let i = 0; for (const builder of this.projectBuilders) { - if (projects[i].status === 'started' || projects[i].status === 'starting') { + if ( + projects[i].status === 'started' || + projects[i].status === 'starting' + ) { await builder.init(); } i++; } } - private async syncMigration(): Promise { - - if (this.config?.toolDir + if ( + this.config?.toolDir // && !('NC_MIGRATIONS_DISABLED' in process.env) ) { - - const dbs = this.config?.envs?.[this.env]?.db + const dbs = this.config?.envs?.[this.env]?.db; if (!dbs || !dbs.length) { - log(`'${this.env}' environment doesn't have any database configuration.`) + log( + `'${this.env}' environment doesn't have any database configuration.` + ); return; } for (const connectionConfig of dbs) { - - log(`Migrations start >> ${connectionConfig?.connection?.['database']} (${connectionConfig.meta?.dbAlias})`) + log( + `Migrations start >> ${connectionConfig?.connection?.['database']} (${connectionConfig.meta?.dbAlias})` + ); try { - /* Update database migrations */ const migrator = new Migrator(); /* initialize migration if folder doesn't exist */ - const migrationFolder = path.join(this.config.toolDir, 'server', 'tool', connectionConfig.meta.dbAlias, 'migrations'); + const migrationFolder = path.join( + this.config.toolDir, + 'server', + 'tool', + connectionConfig.meta.dbAlias, + 'migrations' + ); if (!fs.existsSync(migrationFolder)) { await migrator.init({ folder: this.config?.toolDir, @@ -417,38 +441,40 @@ export default class Noco { env: this.env, dbAlias: connectionConfig.meta.dbAlias, migrationSteps: 99999, - sqlContentMigrate: 1, + sqlContentMigrate: 1 }); - log(`Migrations end << ${connectionConfig?.connection?.['database']} (${connectionConfig.meta?.dbAlias})`) - + log( + `Migrations end << ${connectionConfig?.connection?.['database']} (${connectionConfig.meta?.dbAlias})` + ); } catch (e) { - log(`Migrations Failed !! ${connectionConfig?.connection?.['database']} (${connectionConfig.meta?.dbAlias})`) + log( + `Migrations Failed !! ${connectionConfig?.connection?.['database']} (${connectionConfig.meta?.dbAlias})` + ); console.log(e); // throw e; } } } else { - log('Warning : ignoring migrations on boot since tools directory not defined') + log( + 'Warning : ignoring migrations on boot since tools directory not defined' + ); } } private initWebSocket(): void { - // todo: Auth - this.router.get(`${this.config.dashboardPath}/demo`, (_req, res) => { - (this.ncMeta as any).updateKnex({ - "client": "sqlite3", - "connection": { - "filename": "xcDemo.db" + client: 'sqlite3', + connection: { + filename: 'xcDemo.db' } }); - res.json({msg: 'done'}); - }) + res.json({ msg: 'done' }); + }); this.io = require('socket.io')(); this.io.listen(8083); @@ -459,7 +485,6 @@ export default class Noco { console.log('Disconnected'); this.socketClient = null; }); - }); const statusMonitor = require('express-status-monitor')({ @@ -468,8 +493,10 @@ export default class Noco { }); this.router.use(statusMonitor); - this.router.get(`${this.config.dashboardPath}/status`, statusMonitor.pageRoute) - + this.router.get( + `${this.config.dashboardPath}/status`, + statusMonitor.pageRoute + ); /* title: 'Express Status', // Default title @@ -499,27 +526,24 @@ export default class Noco { }, healthChecks: [], ignoreStartsWith: '/admin'*/ - - } - private async readOrGenJwtSecret(): Promise { if (this.config?.auth?.jwt && !this.config.auth.jwt.secret) { - let secret = (await this.ncMeta.metaGet('', '', 'nc_store', { - key: 'nc_auth_jwt_secret' - }))?.value; + let secret = ( + await this.ncMeta.metaGet('', '', 'nc_store', { + key: 'nc_auth_jwt_secret' + }) + )?.value; if (!secret) { - (await this.ncMeta.metaInsert('', '', 'nc_store', { + await this.ncMeta.metaInsert('', '', 'nc_store', { key: 'nc_auth_jwt_secret', value: secret = uuidv4() - })) + }); } this.config.auth.jwt.secret = secret; } } - - } /** diff --git a/packages/nocodb/src/lib/noco/common/BaseApiBuilder.ts b/packages/nocodb/src/lib/noco/common/BaseApiBuilder.ts index 44c7ac554f..2d6e52adf3 100644 --- a/packages/nocodb/src/lib/noco/common/BaseApiBuilder.ts +++ b/packages/nocodb/src/lib/noco/common/BaseApiBuilder.ts @@ -1,31 +1,33 @@ // import * as fs from "fs"; import debug from 'debug'; -import {Router} from "express"; -import inflection from "inflection"; +import { Router } from 'express'; +import inflection from 'inflection'; // import Knex from "knex"; import { - MysqlClient, PgClient, SqlClient, + MysqlClient, + PgClient, + SqlClient, // SqlClientFactory, Tele } from 'nc-help'; -import XcDynamicChanges from "../../../interface/XcDynamicChanges"; -import {Acls, DbConfig, NcConfig} from "../../../interface/config"; -import {BaseModelSql, XKnex} from "../../dataMapper"; -import ModelXcMetaFactory from "../../sqlMgr/code/models/xc/ModelXcMetaFactory"; +import XcDynamicChanges from '../../../interface/XcDynamicChanges'; +import { Acls, DbConfig, NcConfig } from '../../../interface/config'; +import { BaseModelSql, XKnex } from '../../dataMapper'; +import ModelXcMetaFactory from '../../sqlMgr/code/models/xc/ModelXcMetaFactory'; import ExpressXcPolicy from '../../sqlMgr/code/policies/xc/ExpressXcPolicy'; -import NcHelp from "../../utils/NcHelp"; -import NcProjectBuilder from "../NcProjectBuilder"; -import Noco from "../Noco"; -import NcMetaIO from "../meta/NcMetaIO"; -import XcCache from "../plugins/adapters/cache/XcCache"; - -import BaseModel from "./BaseModel"; -import {XcCron} from "./XcCron"; -import NcConnectionMgr from "./NcConnectionMgr"; -import updateColumnNameInFormula from "./helpers/updateColumnNameInFormula"; -import addErrorOnColumnDeleteInFormula from "./helpers/addErrorOnColumnDeleteInFormula"; +import NcHelp from '../../utils/NcHelp'; +import NcProjectBuilder from '../NcProjectBuilder'; +import Noco from '../Noco'; +import NcMetaIO from '../meta/NcMetaIO'; +import XcCache from '../plugins/adapters/cache/XcCache'; + +import BaseModel from './BaseModel'; +import { XcCron } from './XcCron'; +import NcConnectionMgr from './NcConnectionMgr'; +import updateColumnNameInFormula from './helpers/updateColumnNameInFormula'; +import addErrorOnColumnDeleteInFormula from './helpers/addErrorOnColumnDeleteInFormula'; const log = debug('nc:api:base'); @@ -56,8 +58,8 @@ const IGNORE_TABLES = [ 'nc_shared_views' ]; - -export default abstract class BaseApiBuilder implements XcDynamicChanges { +export default abstract class BaseApiBuilder + implements XcDynamicChanges { public abstract readonly type: string; public get knex(): XKnex { @@ -78,24 +80,22 @@ export default abstract class BaseApiBuilder implements XcDynami public get router(): Router { if (!this.apiRouter) { - this.baseLog(`router : Initializing builder router`) + this.baseLog(`router : Initializing builder router`); this.apiRouter = Router(); // (this.app as any).router.use('/', this.apiRouter) - (this.projectBuilder as any).router.use('/', this.apiRouter) + (this.projectBuilder as any).router.use('/', this.apiRouter); } return this.apiRouter; } public get routeVersionLetter(): string { - return this.connectionConfig?.meta?.api?.prefix || 'v1' + return this.connectionConfig?.meta?.api?.prefix || 'v1'; } - protected get projectId(): string { return this.projectBuilder?.id; } - public get xcModels() { return this.models; } @@ -112,13 +112,13 @@ export default abstract class BaseApiBuilder implements XcDynami event: string; url: string; [key: string]: any; - }> - } - } + }>; + }; + }; public formViews: { - [tableName: string]: any - } + [tableName: string]: any; + }; protected tablesCount = 0; protected relationsCount = 0; @@ -141,13 +141,18 @@ export default abstract class BaseApiBuilder implements XcDynami protected acls: Acls; protected procedureOrFunctionAcls: { - [name: string]: { [role: string]: boolean } + [name: string]: { [role: string]: boolean }; }; protected xcMeta: NcMetaIO; private apiRouter: Router; - constructor(app: T, projectBuilder: NcProjectBuilder, config: NcConfig, connectionConfig: DbConfig) { + constructor( + app: T, + projectBuilder: NcProjectBuilder, + config: NcConfig, + connectionConfig: DbConfig + ) { this.models = {}; this.app = app; this.config = config; @@ -158,7 +163,6 @@ export default abstract class BaseApiBuilder implements XcDynami this.hooks = {}; this.formViews = {}; this.projectBuilder = projectBuilder; - } public getDbType(): any { @@ -177,48 +181,59 @@ export default abstract class BaseApiBuilder implements XcDynami return this.sqlClient; } - public abstract onViewCreate(viewName: string): Promise ; + public abstract onViewCreate(viewName: string): Promise; - public abstract onFunctionCreate(functionName: string): Promise ; + public abstract onFunctionCreate(functionName: string): Promise; - public abstract onProcedureCreate(procedureName: string): Promise ; + public abstract onProcedureCreate(procedureName: string): Promise; - public abstract onViewDelete(viewName: string): Promise ; + public abstract onViewDelete(viewName: string): Promise; - public abstract onProcedureDelete(procedureName: string): Promise ; + public abstract onProcedureDelete(procedureName: string): Promise; - public abstract onFunctionDelete(functionName: string): Promise ; + public abstract onFunctionDelete(functionName: string): Promise; - public abstract onPolicyUpdate(tn: string): Promise ; + public abstract onPolicyUpdate(tn: string): Promise; - public abstract onHandlerCodeUpdate(tn: string): Promise ; + public abstract onHandlerCodeUpdate(tn: string): Promise; - public abstract onMiddlewareCodeUpdate(tn: string): Promise ; + public abstract onMiddlewareCodeUpdate(tn: string): Promise; - public abstract onToggleModels(enabledModels: string[]): Promise ; + public abstract onToggleModels(enabledModels: string[]): Promise; - public abstract onToggleModelRelation(relationInModels: any): Promise ; + public abstract onToggleModelRelation(relationInModels: any): Promise; public async onTableDelete(tn: string): Promise { - this.baseLog(`onTableDelete : '%s'`, tn) + this.baseLog(`onTableDelete : '%s'`, tn); XcCache.del([this.projectId, this.dbAlias, 'table', tn].join('::')); - return this.xcMeta.metaDelete(this.projectId, this.dbAlias, 'nc_relations', null, { - _or: [{ - tn: { - eq: tn - } - }, { - rtn: { - eq: tn - } - },] - }) - await this.deleteTableNameInACL(tn) + return this.xcMeta.metaDelete( + this.projectId, + this.dbAlias, + 'nc_relations', + null, + { + _or: [ + { + tn: { + eq: tn + } + }, + { + rtn: { + eq: tn + } + } + ] + } + ); + await this.deleteTableNameInACL(tn); } - - public async onRelationCreate(tnp: string, tnc: string, args?: any): Promise { - + public async onRelationCreate( + tnp: string, + tnc: string, + args?: any + ): Promise { const { childColumn, onDelete, @@ -232,40 +247,56 @@ export default abstract class BaseApiBuilder implements XcDynami XcCache.del([this.projectId, this.dbAlias, 'table', tnc].join('::')); if (!virtual) { - await this.xcMeta.metaInsert(this.projectId, this.dbAlias, 'nc_relations', { - tn: tnc, - _tn: this.getTableNameAlias(tnc), - cn: childColumn, - rtn: tnp, - _rtn: this.getTableNameAlias(tnp), - rcn: parentColumn, - type: 'real', - db_type: this.connectionConfig?.client, - dr: onDelete, - ur: onUpdate, - fkn - }) + await this.xcMeta.metaInsert( + this.projectId, + this.dbAlias, + 'nc_relations', + { + tn: tnc, + _tn: this.getTableNameAlias(tnc), + cn: childColumn, + rtn: tnp, + _rtn: this.getTableNameAlias(tnp), + rcn: parentColumn, + type: 'real', + db_type: this.connectionConfig?.client, + dr: onDelete, + ur: onUpdate, + fkn + } + ); } else { - await this.xcMeta.metaUpdate(this.projectId, this.dbAlias, 'nc_relations', { - _tn: this.getTableNameAlias(tnc), - _rtn: this.getTableNameAlias(tnp), - }, { - tn: tnc, - cn: childColumn, - rtn: tnp, - rcn: parentColumn, - }) + await this.xcMeta.metaUpdate( + this.projectId, + this.dbAlias, + 'nc_relations', + { + _tn: this.getTableNameAlias(tnc), + _rtn: this.getTableNameAlias(tnp) + }, + { + tn: tnc, + cn: childColumn, + rtn: tnp, + rcn: parentColumn + } + ); } - Tele.emit('evt', {evt_type: 'relation:created'}) - } - - public async onRelationDelete(tnp: string, tnc: string, args: any): Promise { - this.baseLog(`onRelationDelete : Within relation delete handler of '%s' => '%s'`, tnp, tnc); + Tele.emit('evt', { evt_type: 'relation:created' }); + } + + public async onRelationDelete( + tnp: string, + tnc: string, + args: any + ): Promise { + this.baseLog( + `onRelationDelete : Within relation delete handler of '%s' => '%s'`, + tnp, + tnc + ); - const { - childColumn, - parentColumn, - } = args; + const { childColumn, parentColumn } = args; await this.xcMeta.metaDelete(this.projectId, this.dbAlias, 'nc_relations', { tn: tnc, @@ -274,7 +305,7 @@ export default abstract class BaseApiBuilder implements XcDynami rcn: parentColumn, type: 'real', db_type: this.connectionConfig?.client - }) + }); await this.deleteRelationInACL(tnp, tnc); @@ -282,33 +313,64 @@ export default abstract class BaseApiBuilder implements XcDynami XcCache.del([this.projectId, this.dbAlias, 'table', tnp].join('::')); } - - public async onTableRename(oldTableName: string, newTableName: string): Promise { + public async onTableRename( + oldTableName: string, + newTableName: string + ): Promise { this.baseLog(`onTableRename : '%s' => '%s'`, oldTableName, newTableName); - this.baseLog(`onTableRename : updating table name in hooks meta table - '%s' => '%s'`, oldTableName, newTableName); - XcCache.del([this.projectId, this.dbAlias, 'table', oldTableName].join('::')); - await this.xcMeta.metaUpdate(this.projectId, this.dbAlias, 'nc_relations', { - tn: newTableName - }, { - tn: oldTableName - }) - await this.xcMeta.metaUpdate(this.projectId, this.dbAlias, 'nc_relations', { - rtn: newTableName - }, { - rtn: oldTableName - }) - - await this.xcMeta.metaUpdate(this.projectId, this.dbAlias, 'nc_hooks', { - tn: newTableName - }, { - tn: oldTableName - }) + this.baseLog( + `onTableRename : updating table name in hooks meta table - '%s' => '%s'`, + oldTableName, + newTableName + ); + XcCache.del( + [this.projectId, this.dbAlias, 'table', oldTableName].join('::') + ); + await this.xcMeta.metaUpdate( + this.projectId, + this.dbAlias, + 'nc_relations', + { + tn: newTableName + }, + { + tn: oldTableName + } + ); + await this.xcMeta.metaUpdate( + this.projectId, + this.dbAlias, + 'nc_relations', + { + rtn: newTableName + }, + { + rtn: oldTableName + } + ); + await this.xcMeta.metaUpdate( + this.projectId, + this.dbAlias, + 'nc_hooks', + { + tn: newTableName + }, + { + tn: oldTableName + } + ); /* Update virtual views */ - await this.xcMeta.metaUpdate(this.projectId, this.dbAlias, 'nc_models', { - parent_model_title: newTableName, - }, {'parent_model_title': oldTableName, type: 'vtable'}) + await this.xcMeta.metaUpdate( + this.projectId, + this.dbAlias, + 'nc_models', + { + parent_model_title: newTableName + }, + { parent_model_title: oldTableName, type: 'vtable' } + ); await this.loadHooks(); await this.loadFormViews(); @@ -316,17 +378,25 @@ export default abstract class BaseApiBuilder implements XcDynami await this.modifyTableNameInACL(oldTableName, newTableName); } - public async onGqlSchemaUpdate(_tableName: string, _schema: string): Promise { - throw new Error('`onGqlSchemaUpdate` not implemented') + public async onGqlSchemaUpdate( + _tableName: string, + _schema: string + ): Promise { + throw new Error('`onGqlSchemaUpdate` not implemented'); } // todo: change name to meta uodate public async onMetaUpdate(tn: string): Promise { this.baseLog(`onValidationUpdate : '%s'`, tn); - const modelRow = await this.xcMeta.metaGet(this.projectId, this.dbAlias, 'nc_models', { - title: tn, - type: 'table' - }); + const modelRow = await this.xcMeta.metaGet( + this.projectId, + this.dbAlias, + 'nc_models', + { + title: tn, + type: 'table' + } + ); if (!modelRow) { return; @@ -334,7 +404,10 @@ export default abstract class BaseApiBuilder implements XcDynami const metaObj = JSON.parse(modelRow.meta); this.metas[tn] = metaObj; - this.baseLog(`onValidationUpdate : Generating model instance for '%s' table`, tn) + this.baseLog( + `onValidationUpdate : Generating model instance for '%s' table`, + tn + ); this.models[modelRow.title] = this.getBaseModel(metaObj); XcCache.del([this.projectId, this.dbAlias, 'table', tn].join('::')); @@ -344,67 +417,95 @@ export default abstract class BaseApiBuilder implements XcDynami // await this.onTableRename(tn, tn) } - - public async onTableUpdate(changeObj: any, beforeMetaUpdate?: (args: any) => Promise): Promise { + public async onTableUpdate( + changeObj: any, + beforeMetaUpdate?: (args: any) => Promise + ): Promise { const tn = changeObj.tn; this.baseLog(`onTableUpdate : '%s'`, tn); - this.baseLog(`onTableUpdate : Getting old model meta for '%s'`, tn) + this.baseLog(`onTableUpdate : Getting old model meta for '%s'`, tn); XcCache.del([this.projectId, this.dbAlias, 'table', tn].join('::')); const relationTableMetas: Set = new Set(); - const oldModelRow = await this.xcMeta.metaGet(this.projectId, this.dbAlias, 'nc_models', { - title: tn - }) + const oldModelRow = await this.xcMeta.metaGet( + this.projectId, + this.dbAlias, + 'nc_models', + { + title: tn + } + ); let queryParams: any; try { queryParams = JSON.parse(oldModelRow.query_params); } catch (e) { - queryParams = {} + queryParams = {}; } - if (!oldModelRow) { return; } // todo : optimize db operations - const columns = changeObj.columns - .filter(c => c.altered !== 4) - .map(({altered: _al, ...rest}) => rest) || await this.getColumnList(tn); + const columns = + changeObj.columns + .filter(c => c.altered !== 4) + .map(({ altered: _al, ...rest }) => rest) || + (await this.getColumnList(tn)); /* Get all relations */ const relations = await this.relationsSyncAndGet(); const belongsTo = this.extractBelongsToRelationsOfTable(relations, tn); const hasMany = this.extractHasManyRelationsOfTable(relations, tn); - - const virtualViews = await this.xcMeta.metaList(this.projectId, this.dbAlias, 'nc_models', { - condition: { - type: 'vtable', - parent_model_title: tn + const virtualViews = await this.xcMeta.metaList( + this.projectId, + this.dbAlias, + 'nc_models', + { + condition: { + type: 'vtable', + parent_model_title: tn + } } - }); + ); const virtualViewsParamsArr = virtualViews.map(v => { try { return JSON.parse(v.query_params); - } catch (e) { - } - return {} - }) + } catch (e) {} + return {}; + }); - const ctx = this.generateContextForTable(tn, columns, [...hasMany, ...belongsTo], hasMany, belongsTo); + const ctx = this.generateContextForTable( + tn, + columns, + [...hasMany, ...belongsTo], + hasMany, + belongsTo + ); - this.baseLog(`onTableUpdate : Generating new model meta for '%s' table`, tn) + this.baseLog( + `onTableUpdate : Generating new model meta for '%s' table`, + tn + ); /* create models from table */ - const newMeta: any = ModelXcMetaFactory.create(this.connectionConfig, {dir: '', ctx, filename: ''}).getObject(); - + const newMeta: any = ModelXcMetaFactory.create(this.connectionConfig, { + dir: '', + ctx, + filename: '' + }).getObject(); /* get ACL row */ - const aclRow = await this.xcMeta.metaGet(this.projectId, this.dbAlias, 'nc_acl', {tn}); + const aclRow = await this.xcMeta.metaGet( + this.projectId, + this.dbAlias, + 'nc_acl', + { tn } + ); const acl = JSON.parse(aclRow.acl); const oldMeta = JSON.parse(oldModelRow.meta); @@ -415,52 +516,71 @@ export default abstract class BaseApiBuilder implements XcDynami const aclOper = []; - this.baseLog(`onTableUpdate : Comparing and updating new metadata of '%s' table`, tn) + this.baseLog( + `onTableUpdate : Comparing and updating new metadata of '%s' table`, + tn + ); for (const column of changeObj.columns) { let oldCol; let newCol; // column update if (column.altered === 8 || column.altered === 2) { - - oldCol = oldMeta.columns.find(c => c.cn === column.cno); newCol = newMeta.columns.find(c => c.cn === column.cn); - if (newCol && oldCol && column.dt === oldCol.dt && !newCol?.validate?.func?.length) { + if ( + newCol && + oldCol && + column.dt === oldCol.dt && + !newCol?.validate?.func?.length + ) { newCol.validate = oldCol.validate; } // column rename if (column.cno !== column.cn) { - updateColumnNameInFormula({ virtualColumns: newMeta.v, oldColumnName: oldCol.cn, - newColumnName: newCol.cn, - }) + newColumnName: newCol.cn + }); // todo: populate alias newCol._cn = newCol.cn; - await this.xcMeta.metaUpdate(this.projectId, this.dbAlias, 'nc_relations', { - cn: column.cn - }, { - cn: column.cno, - tn - }) - await this.xcMeta.metaUpdate(this.projectId, this.dbAlias, 'nc_relations', { - rcn: column.cn - }, { - rcn: column.cno, - rtn: tn - }) + await this.xcMeta.metaUpdate( + this.projectId, + this.dbAlias, + 'nc_relations', + { + cn: column.cn + }, + { + cn: column.cno, + tn + } + ); + await this.xcMeta.metaUpdate( + this.projectId, + this.dbAlias, + 'nc_relations', + { + rcn: column.cn + }, + { + rcn: column.cno, + rtn: tn + } + ); - aclOper.push(async () => this.modifyColumnNameInACL(tn, column.cno, column.cn)); + aclOper.push(async () => + this.modifyColumnNameInACL(tn, column.cno, column.cn) + ); // virtual views param update for (const qp of [queryParams, ...virtualViewsParamsArr]) { - if (!qp) continue + if (!qp) continue; // @ts-ignore - const {filters, sortList, showFields} = qp; + const { filters, sortList, showFields } = qp; /* update sort field */ const s = sortList.find(v => v.field === column.cno); if (s) { @@ -486,7 +606,7 @@ export default abstract class BaseApiBuilder implements XcDynami bt._cn = column._cn; // update column name in parent table metadata - relationTableMetas.add(this.metas[bt.rtn]) + relationTableMetas.add(this.metas[bt.rtn]); for (const pHm of this.metas[bt.rtn]?.hasMany) { if (pHm.cn === column.cno && pHm.tn === tn) { pHm.cn = column.cn; @@ -495,19 +615,17 @@ export default abstract class BaseApiBuilder implements XcDynami } } - // update lookup columns this.metas[bt.rtn].v?.forEach(v => { if (v.lk && v.lk.ltn === tn && v.lk.lcn === column.cno) { - relationTableMetas.add(this.metas[bt.rtn]) + relationTableMetas.add(this.metas[bt.rtn]); v.lk.lcn = column.cn; v.lk._lcn = column._cn; } - }) + }); } } - // update column name in has many if (newMeta.hasMany?.length) { for (const hm of newMeta.hasMany) { @@ -516,7 +634,7 @@ export default abstract class BaseApiBuilder implements XcDynami hm._rcn = column._cn; // update column name in child table metadata - relationTableMetas.add(this.metas[hm.tn]) + relationTableMetas.add(this.metas[hm.tn]); for (const cBt of this.metas[hm.tn]?.belongsTo) { if (cBt.rcn === column.cno && cBt.rtn === tn) { cBt.rcn = column.cn; @@ -528,12 +646,11 @@ export default abstract class BaseApiBuilder implements XcDynami // update lookup columns this.metas[hm.tn].v?.forEach(v => { if (v.lk && v.lk.ltn === tn && v.lk.lcn === column.cno) { - relationTableMetas.add(this.metas[hm.tn]) + relationTableMetas.add(this.metas[hm.tn]); v.lk.lcn = column.cn; v.lk._lcn = column._cn; } - }) - + }); } } @@ -545,7 +662,7 @@ export default abstract class BaseApiBuilder implements XcDynami mm._cn = column._cn; // update column name in child table metadata - relationTableMetas.add(this.metas[mm.rtn]) + relationTableMetas.add(this.metas[mm.rtn]); for (const cMm of this.metas[mm.rtn]?.manyToMany) { if (cMm.rcn === column.cno && cMm.rtn === tn) { cMm.rcn = column.cn; @@ -554,16 +671,14 @@ export default abstract class BaseApiBuilder implements XcDynami } } - // update lookup columns this.metas[mm.rtn].v?.forEach(v => { if (v.lk && v.lk.ltn === tn && v.lk.lcn === column.cno) { - relationTableMetas.add(this.metas[mm.rtn]) + relationTableMetas.add(this.metas[mm.rtn]); v.lk.lcn = column.cn; v.lk._lcn = column._cn; } - }) - + }); } } } @@ -579,7 +694,6 @@ export default abstract class BaseApiBuilder implements XcDynami } } } - } else if (column.altered === 4) { // handle delete col -- no change for (const permObj of Object.values(acl)) { @@ -596,15 +710,14 @@ export default abstract class BaseApiBuilder implements XcDynami addErrorOnColumnDeleteInFormula({ virtualColumns: newMeta.v, columnName: column.cno - }) + }); aclOper.push(async () => this.deleteColumnNameInACL(tn, column.cno)); - // virtual views param update for (const qp of virtualViewsParamsArr) { // @ts-ignore - const {filters, sortList, showFields} = qp; + const { filters, sortList, showFields } = qp; /* update sort field */ const sIndex = sortList.findIndex(v => v.field === column.cno); if (sIndex > -1) { @@ -620,7 +733,6 @@ export default abstract class BaseApiBuilder implements XcDynami } } - // Delete lookup columns mapping to current column // update column name in belongs to if (newMeta.belongsTo?.length) { @@ -628,26 +740,25 @@ export default abstract class BaseApiBuilder implements XcDynami // filter out lookup columns which maps to current col this.metas[bt.rtn].v = this.metas[bt.rtn].v?.filter(v => { if (v.lk && v.lk.ltn === tn && v.lk.lcn === column.cn) { - relationTableMetas.add(this.metas[bt.rtn]) + relationTableMetas.add(this.metas[bt.rtn]); return false; } return true; - }) + }); } } - // update column name in has many if (newMeta.hasMany?.length) { for (const hm of newMeta.hasMany) { // filter out lookup columns which maps to current col this.metas[hm.tn].v = this.metas[hm.tn].v?.filter(v => { if (v.lk && v.lk.ltn === tn && v.lk.lcn === column.cn) { - relationTableMetas.add(this.metas[hm.tn]) + relationTableMetas.add(this.metas[hm.tn]); return false; } - return true - }) + return true; + }); } } @@ -657,15 +768,13 @@ export default abstract class BaseApiBuilder implements XcDynami // filter out lookup columns which maps to current col this.metas[mm.rtn].v = this.metas[mm.rtn].v?.filter(v => { if (v.lk && v.lk.ltn === tn && v.lk.lcn === column.cn) { - relationTableMetas.add(this.metas[mm.rtn]) + relationTableMetas.add(this.metas[mm.rtn]); return false; } return true; - }) + }); } } - - } else if (column.altered === 1) { // handle new col -- no change for (const permObj of Object.values(acl)) { @@ -680,8 +789,6 @@ export default abstract class BaseApiBuilder implements XcDynami if (queryParams?.showFields) { queryParams.showFields[column.cno] = true; } - - } else { oldCol = oldMeta.columns.find(c => c.cn === column.cn); newCol = newMeta.columns.find(c => c.cn === column.cn); @@ -691,39 +798,64 @@ export default abstract class BaseApiBuilder implements XcDynami } for (let i = 0; i < virtualViewsParamsArr.length; i++) { - await this.xcMeta.metaUpdate(this.projectId, this.dbAlias, 'nc_models', { - query_params: JSON.stringify(virtualViewsParamsArr[i]) - }, virtualViews[i].id); + await this.xcMeta.metaUpdate( + this.projectId, + this.dbAlias, + 'nc_models', + { + query_params: JSON.stringify(virtualViewsParamsArr[i]) + }, + virtualViews[i].id + ); } } - // update relation tables metadata for (const relMeta of relationTableMetas) { - await this.xcMeta.metaUpdate(this.projectId, this.dbAlias, 'nc_models', { - meta: JSON.stringify(relMeta) - }, { - title: relMeta.tn - }); + await this.xcMeta.metaUpdate( + this.projectId, + this.dbAlias, + 'nc_models', + { + meta: JSON.stringify(relMeta) + }, + { + title: relMeta.tn + } + ); this.models[relMeta.tn] = this.getBaseModel(relMeta); - XcCache.del([this.projectId, this.dbAlias, 'table', relMeta.tn].join('::')); + XcCache.del( + [this.projectId, this.dbAlias, 'table', relMeta.tn].join('::') + ); } - await this.xcMeta.metaUpdate(this.projectId, this.dbAlias, 'nc_models', { - meta: JSON.stringify(newMeta), - ...(queryParams ? {query_params: JSON.stringify(queryParams)} : {}) - }, { - title: tn - }); + await this.xcMeta.metaUpdate( + this.projectId, + this.dbAlias, + 'nc_models', + { + meta: JSON.stringify(newMeta), + ...(queryParams ? { query_params: JSON.stringify(queryParams) } : {}) + }, + { + title: tn + } + ); XcCache.del([this.projectId, this.dbAlias, 'table', tn].join('::')); this.models[tn] = this.getBaseModel(newMeta); - await this.xcMeta.metaUpdate(this.projectId, this.dbAlias, 'nc_acl', { - acl: JSON.stringify(acl) - }, { - tn - }); + await this.xcMeta.metaUpdate( + this.projectId, + this.dbAlias, + 'nc_acl', + { + acl: JSON.stringify(acl) + }, + { + tn + } + ); this.acls[tn] = acl; @@ -733,22 +865,32 @@ export default abstract class BaseApiBuilder implements XcDynami meta: newMeta }); } - this.baseLog(`onTableUpdate : Generating model instance for '%s' table`, tn) - + this.baseLog( + `onTableUpdate : Generating model instance for '%s' table`, + tn + ); await NcHelp.executeOperations(aclOper, this.connectionConfig.client); - - } - - public async onViewUpdate(viewName: string, beforeMetaUpdate?: (args: any) => Promise): Promise { + public async onViewUpdate( + viewName: string, + beforeMetaUpdate?: (args: any) => Promise + ): Promise { this.baseLog(`onViewUpdate : '%s'`, viewName); - this.baseLog(`onViewUpdate : Getting old model meta of '%s' view`, viewName) + this.baseLog( + `onViewUpdate : Getting old model meta of '%s' view`, + viewName + ); - const oldModelRow = this.xcMeta.metaGet(this.projectId, this.dbAlias, 'nc_models', { - title: viewName - }); + const oldModelRow = this.xcMeta.metaGet( + this.projectId, + this.dbAlias, + 'nc_models', + { + title: viewName + } + ); if (!oldModelRow) { return; @@ -757,21 +899,40 @@ export default abstract class BaseApiBuilder implements XcDynami // todo : optimize db operations const columns = await this.getColumnList(viewName); + const ctx = this.generateContextForTable( + viewName, + columns, + [], + [], + [], + 'view' + ); - const ctx = this.generateContextForTable(viewName, columns, [], [], [], 'view'); - - this.baseLog(`onViewUpdate : Generating new model meta of '%s' view`, viewName) + this.baseLog( + `onViewUpdate : Generating new model meta of '%s' view`, + viewName + ); /* create models from table */ - const newMeta = ModelXcMetaFactory.create(this.connectionConfig, {dir: '', ctx, filename: ''}).getObject(); - - this.baseLog(`onViewUpdate : Updating model meta of '%s' view`, viewName) - - await this.xcMeta.metaUpdate(this.projectId, this.dbAlias, 'nc_models', { - meta: JSON.stringify(newMeta) - }, { - title: viewName - }); + const newMeta = ModelXcMetaFactory.create(this.connectionConfig, { + dir: '', + ctx, + filename: '' + }).getObject(); + + this.baseLog(`onViewUpdate : Updating model meta of '%s' view`, viewName); + + await this.xcMeta.metaUpdate( + this.projectId, + this.dbAlias, + 'nc_models', + { + meta: JSON.stringify(newMeta) + }, + { + title: viewName + } + ); if (beforeMetaUpdate) { await beforeMetaUpdate({ @@ -779,50 +940,52 @@ export default abstract class BaseApiBuilder implements XcDynami meta: newMeta }); } - this.baseLog(`onViewUpdate : Generating model instance for '%s' table`, viewName) + this.baseLog( + `onViewUpdate : Generating model instance for '%s' table`, + viewName + ); this.models[viewName] = this.getBaseModel(newMeta); - - } - public getDbDriver(): XKnex { this.initDbDriver(); return this.dbDriver; } - public async onHooksUpdate(tn?: string): Promise { - this.baseLog(`onHooksUpdate : %s`, tn) + this.baseLog(`onHooksUpdate : %s`, tn); await this.loadHooks(); } public async restartCron(args): Promise { - this.baseLog(`restartCron :`,) + this.baseLog(`restartCron :`); await this.cronJob.restartCron(args); } public async removeCron(args): Promise { - this.baseLog(`removeCron :`,) + this.baseLog(`removeCron :`); await this.cronJob.removeCron(args); } - public async xcUpgrade(): Promise { - this.baseLog(`xcUpgrade :`,) - + this.baseLog(`xcUpgrade :`); const NC_VERSIONS = [ - {name: '0009000', handler: null}, - {name: '0009044', handler: this.ncUpManyToMany.bind(this)} - ] - if (!await this.xcMeta?.knex?.schema?.hasTable?.('nc_store')) { + { name: '0009000', handler: null }, + { name: '0009044', handler: this.ncUpManyToMany.bind(this) } + ]; + if (!(await this.xcMeta?.knex?.schema?.hasTable?.('nc_store'))) { return; } - this.baseLog(`xcUpgrade : Getting configuration from meta database`,) + this.baseLog(`xcUpgrade : Getting configuration from meta database`); - const config = await this.xcMeta.metaGet(this.projectId, this.dbAlias, 'nc_store', {key: 'NC_CONFIG'}); + const config = await this.xcMeta.metaGet( + this.projectId, + this.dbAlias, + 'nc_store', + { key: 'NC_CONFIG' } + ); if (config) { const configObj: NcConfig = JSON.parse(config.value); @@ -830,19 +993,28 @@ export default abstract class BaseApiBuilder implements XcDynami for (const version of NC_VERSIONS) { // compare current version and old version if (version.name > configObj.version) { - this.baseLog(`xcUpgrade : Upgrading '%s' => '%s'`, configObj.version, version.name) + this.baseLog( + `xcUpgrade : Upgrading '%s' => '%s'`, + configObj.version, + version.name + ); await version?.handler?.(); // update version in meta after each upgrade config.version = version.name; - await this.xcMeta.metaUpdate(this.projectId, this.dbAlias, 'nc_store', { - value: JSON.stringify(config) - }, { - key: 'NC_CONFIG', - }); + await this.xcMeta.metaUpdate( + this.projectId, + this.dbAlias, + 'nc_store', + { + value: JSON.stringify(config) + }, + { + key: 'NC_CONFIG' + } + ); // todo: backup data - } if (version.name === process.env.NC_VERSION) { break; @@ -855,10 +1027,12 @@ export default abstract class BaseApiBuilder implements XcDynami }); } } else { - this.baseLog(`xcUpgrade : Inserting config to meta database`,) + this.baseLog(`xcUpgrade : Inserting config to meta database`); const configObj: NcConfig = JSON.parse(JSON.stringify(this.config)); delete configObj.envs; - const isOld = (await this.xcMeta.metaList(this.projectId, this.dbAlias, 'nc_models'))?.length; + const isOld = ( + await this.xcMeta.metaList(this.projectId, this.dbAlias, 'nc_models') + )?.length; configObj.version = isOld ? '0009000' : process.env.NC_VERSION; await this.xcMeta.metaInsert(this.projectId, this.dbAlias, 'nc_store', { key: 'NC_CONFIG', @@ -870,11 +1044,15 @@ export default abstract class BaseApiBuilder implements XcDynami } } - public async onAclUpdate(tn: string): Promise { this.baseLog(`onAclUpdate : '%s'`, tn); - this.baseLog(`onAclUpdate : Loading latest acl for '%s'`, tn) - const aclRow = await this.xcMeta.metaGet(this.projectId, this.dbAlias, 'nc_acl', {'tn': tn}); + this.baseLog(`onAclUpdate : Loading latest acl for '%s'`, tn); + const aclRow = await this.xcMeta.metaGet( + this.projectId, + this.dbAlias, + 'nc_acl', + { tn: tn } + ); if (aclRow) { if (aclRow.type === 'procedure' || aclRow.type === 'function') { this.procedureOrFunctionAcls[tn] = JSON.parse(aclRow.acl); @@ -887,8 +1065,12 @@ export default abstract class BaseApiBuilder implements XcDynami // NOTE: xc-meta public async loadXcAcl(): Promise { this.baseLog(`loadXcAcl :`); - const aclRows = await this.xcMeta.metaList(this.projectId, this.dbAlias, 'nc_acl'); - for (const {acl, tn, type} of aclRows) { + const aclRows = await this.xcMeta.metaList( + this.projectId, + this.dbAlias, + 'nc_acl' + ); + for (const { acl, tn, type } of aclRows) { if (type === 'procedure' || type === 'function') { this.procedureOrFunctionAcls[tn] = JSON.parse(acl); } else { @@ -901,7 +1083,6 @@ export default abstract class BaseApiBuilder implements XcDynami return this.xcMeta; } - public async xcTablesRowDelete(tn: string): Promise { await this.xcMeta.metaDelete(this.projectId, this.dbAlias, 'nc_models', { title: tn @@ -911,51 +1092,100 @@ export default abstract class BaseApiBuilder implements XcDynami }); } - public async onVirtualRelationCreate(parentTable: string, childTable: string): Promise { - return this.xcMeta.metaUpdate(this.projectId, this.dbAlias, 'nc_relations', { - _tn: this.getTableNameAlias(childTable), - _rtn: this.getTableNameAlias(parentTable), - }, { - tn: childTable, - rtn: parentTable, - }); + public async onVirtualRelationCreate( + parentTable: string, + childTable: string + ): Promise { + return this.xcMeta.metaUpdate( + this.projectId, + this.dbAlias, + 'nc_relations', + { + _tn: this.getTableNameAlias(childTable), + _rtn: this.getTableNameAlias(parentTable) + }, + { + tn: childTable, + rtn: parentTable + } + ); } - - public async onManyToManyRelationCreate(parent: string, child: string, _args?: any) { - return this.getManyToManyRelations({parent, child}) + public async onManyToManyRelationCreate( + parent: string, + child: string, + _args?: any + ) { + return this.getManyToManyRelations({ parent, child }); } - public async onManyToManyRelationDelete(parent: string, child: string, _args?: any) { - + public async onManyToManyRelationDelete( + parent: string, + child: string, + _args?: any + ) { const parentMeta = this.metas[parent]; const childMeta = this.metas[child]; - parentMeta.manyToMany = parentMeta.manyToMany.filter(mm => !(mm.tn === parent && mm.rtn === child || mm.tn === child && mm.rtn === parent)) - childMeta.manyToMany = childMeta.manyToMany.filter(mm => !(mm.tn === parent && mm.rtn === child || mm.tn === child && mm.rtn === parent)) + parentMeta.manyToMany = parentMeta.manyToMany.filter( + mm => + !( + (mm.tn === parent && mm.rtn === child) || + (mm.tn === child && mm.rtn === parent) + ) + ); + childMeta.manyToMany = childMeta.manyToMany.filter( + mm => + !( + (mm.tn === parent && mm.rtn === child) || + (mm.tn === child && mm.rtn === parent) + ) + ); // filter lookup and relation virtual columns - parentMeta.v = parentMeta.v.filter(({ - mm, - ...rest - }) => (!mm || !(mm.tn === parent && mm.rtn === child || mm.tn === child && mm.rtn === parent)) - // check for lookup - && !(rest.lk && rest.lk.type === 'mm' && (rest.lk.tn === parent && rest.lk.rtn === child || rest.lk.tn === child && rest.lk.rtn === parent)) - ) - childMeta.v = childMeta.v.filter(({ - mm, - ...rest - }) => (!mm || !(mm.tn === parent && mm.rtn === child || mm.tn === child && mm.rtn === parent)) - // check for lookup - && !(rest.lk && rest.lk.type === 'mm' && (rest.lk.tn === parent && rest.lk.rtn === child || rest.lk.tn === child && rest.lk.rtn === parent)) - ) + parentMeta.v = parentMeta.v.filter( + ({ mm, ...rest }) => + (!mm || + !( + (mm.tn === parent && mm.rtn === child) || + (mm.tn === child && mm.rtn === parent) + )) && + // check for lookup + !( + rest.lk && + rest.lk.type === 'mm' && + ((rest.lk.tn === parent && rest.lk.rtn === child) || + (rest.lk.tn === child && rest.lk.rtn === parent)) + ) + ); + childMeta.v = childMeta.v.filter( + ({ mm, ...rest }) => + (!mm || + !( + (mm.tn === parent && mm.rtn === child) || + (mm.tn === child && mm.rtn === parent) + )) && + // check for lookup + !( + rest.lk && + rest.lk.type === 'mm' && + ((rest.lk.tn === parent && rest.lk.rtn === child) || + (rest.lk.tn === child && rest.lk.rtn === parent)) + ) + ); for (const meta of [parentMeta, childMeta]) { - await this.xcMeta.metaUpdate(this.projectId, this.dbAlias, 'nc_models', { - meta: JSON.stringify(meta) - }, {title: meta.tn}) + await this.xcMeta.metaUpdate( + this.projectId, + this.dbAlias, + 'nc_models', + { + meta: JSON.stringify(meta) + }, + { title: meta.tn } + ); XcCache.del([this.projectId, this.dbAlias, 'table', meta.tn].join('::')); - this.models[meta.tn] = this.getBaseModel(meta) + this.models[meta.tn] = this.getBaseModel(meta); } } @@ -968,12 +1198,16 @@ export default abstract class BaseApiBuilder implements XcDynami } public async onVirtualColumnAliasUpdate(tableName: string): Promise { - const model = await this.xcMeta.metaGet(this.projectId, this.dbAlias, 'nc_models', {title: tableName}); + const model = await this.xcMeta.metaGet( + this.projectId, + this.dbAlias, + 'nc_models', + { title: tableName } + ); const meta = JSON.parse(model.meta); this.models[tableName] = this.getBaseModel(meta); } - protected async loadCommon(): Promise { this.baseLog(`loadCommon :`); @@ -981,7 +1215,6 @@ export default abstract class BaseApiBuilder implements XcDynami await this.cronJob.init(); } - protected initDbDriver(): void { this.dbDriver = NcConnectionMgr.get({ dbAlias: this.dbAlias, @@ -994,7 +1227,7 @@ export default abstract class BaseApiBuilder implements XcDynami env: this.config.env, config: this.config, projectId: this.projectId - }) + }); // if (!this.dbDriver) { // if(this.projectBuilder?.prefix){ // this.dbDriver = this.xcMeta.knex @@ -1048,23 +1281,23 @@ export default abstract class BaseApiBuilder implements XcDynami // } } - // table alias functions protected getInflectedName(name: string, inflectionFns: string): string { if (inflectionFns && inflectionFns !== 'none') { - return inflectionFns.split(',').reduce((out, fn) => inflection[fn](out), name); + return inflectionFns + .split(',') + .reduce((out, fn) => inflection[fn](out), name); } return name; } protected async getColumnList(tn: string): Promise { this.baseLog(`getColumnList : '%s'`, tn); - let columns = await this.sqlClient.columnList({tn}); + let columns = await this.sqlClient.columnList({ tn }); columns = columns.data.list; return columns; } - protected async getRelationList(): Promise { this.baseLog(`getRelationList :`); @@ -1076,26 +1309,52 @@ export default abstract class BaseApiBuilder implements XcDynami protected async getXcRelationList(): Promise { this.baseLog(`getRelationList :`); - const relations = await this.xcMeta.metaList(this.projectId, this.dbAlias, 'nc_relations'); + const relations = await this.xcMeta.metaList( + this.projectId, + this.dbAlias, + 'nc_relations' + ); return relations; } - protected extractHasManyRelationsOfTable(relations, tn: string, enabledModels?: string[]): any[] { + protected extractHasManyRelationsOfTable( + relations, + tn: string, + enabledModels?: string[] + ): any[] { this.baseLog(`extractHasManyRelationsOfTable : '%s'`, tn); - const hasManyRel = relations.filter(r => (r.rtn === tn && (!enabledModels || enabledModels.includes(r.tn)))); + const hasManyRel = relations.filter( + r => r.rtn === tn && (!enabledModels || enabledModels.includes(r.tn)) + ); const hasMany = JSON.parse(JSON.stringify(hasManyRel)); return hasMany; } - - protected extractBelongsToRelationsOfTable(relations: any[], tn: string, enabledModels?: string[]): any[] { + protected extractBelongsToRelationsOfTable( + relations: any[], + tn: string, + enabledModels?: string[] + ): any[] { this.baseLog(`extractBelongsToRelationsOfTable : '%s'`, tn); - const belongsTo = JSON.parse(JSON.stringify(relations.filter(r => (r.tn === tn) && (!enabledModels || enabledModels.includes(r.rtn))))); + const belongsTo = JSON.parse( + JSON.stringify( + relations.filter( + r => r.tn === tn && (!enabledModels || enabledModels.includes(r.rtn)) + ) + ) + ); return belongsTo; } - - protected generateContextForTable(tn: string, columns: any[], relations, hasMany: any[], belongsTo: any[], type = 'table', tableNameAlias?: string): any { + protected generateContextForTable( + tn: string, + columns: any[], + relations, + hasMany: any[], + belongsTo: any[], + type = 'table', + tableNameAlias?: string + ): any { this.baseLog(`generateContextForTable : '%s' %s`, tn, type); for (const col of columns) { @@ -1103,7 +1362,7 @@ export default abstract class BaseApiBuilder implements XcDynami } // tslint:disable-next-line:variable-name - const _tn = tableNameAlias || this.getTableNameAlias(tn) + const _tn = tableNameAlias || this.getTableNameAlias(tn); const ctx = { dbType: this.connectionConfig.client, @@ -1130,11 +1389,14 @@ export default abstract class BaseApiBuilder implements XcDynami } if (this.projectBuilder?.prefix) { - tn = tn.replace(this.projectBuilder?.prefix, '') + tn = tn.replace(this.projectBuilder?.prefix, ''); } - const modifiedTableName = tn?.replace(/^(?=\d+)/, 'ISN___') - return this.getInflectedName(modifiedTableName, this.connectionConfig?.meta?.inflection?.tn); + const modifiedTableName = tn?.replace(/^(?=\d+)/, 'ISN___'); + return this.getInflectedName( + modifiedTableName, + this.connectionConfig?.meta?.inflection?.tn + ); } protected generateContextForHasMany(ctx, tnc: string): any { @@ -1148,7 +1410,6 @@ export default abstract class BaseApiBuilder implements XcDynami }; } - protected generateContextForBelongsTo(ctx: any, rtn: string): any { this.baseLog(`generateContextForBelongsTo : '%s' => '%s'`, rtn, ctx.tn); return { @@ -1160,58 +1421,76 @@ export default abstract class BaseApiBuilder implements XcDynami }; } - protected generateRendererArgs(ctx: any): any { this.baseLog(`generateRendererArgs : '%s'`, ctx.tn); - return {dir: '', ctx, filename: ''}; + return { dir: '', ctx, filename: '' }; } - - protected getRelationTableNames(relations, newTablename: string, enabledTableList?: string[]): string[] { + protected getRelationTableNames( + relations, + newTablename: string, + enabledTableList?: string[] + ): string[] { this.baseLog(`getRelationTableNames : '%s'`, newTablename); const relatedTableList = []; // get all relation table names for (const r of relations) { - if (newTablename === r.tn && (!enabledTableList || enabledTableList.includes(r.rtn))) { + if ( + newTablename === r.tn && + (!enabledTableList || enabledTableList.includes(r.rtn)) + ) { if (!relatedTableList.includes(r.rtn)) { - relatedTableList.push(r.rtn) + relatedTableList.push(r.rtn); } - } else if (newTablename === r.rtn && (!enabledTableList || enabledTableList.includes(r.tn))) { + } else if ( + newTablename === r.rtn && + (!enabledTableList || enabledTableList.includes(r.tn)) + ) { if (!relatedTableList.includes(r.tn)) { - relatedTableList.push(r.tn) + relatedTableList.push(r.tn); } } } return relatedTableList; } - protected filterRelationsForTable(relations: any[], tn: string, enabledModels?: string[]): any[] { + protected filterRelationsForTable( + relations: any[], + tn: string, + enabledModels?: string[] + ): any[] { this.baseLog(`filterRelationsForTable : '%s'`, tn); - const tableRelations = relations.filter(r => ( - (r.tn === tn && (!enabledModels || enabledModels.includes(r.rtn))) - || (r.rtn === tn && (!enabledModels || enabledModels.includes(r.tn))) - ) + const tableRelations = relations.filter( + r => + (r.tn === tn && (!enabledModels || enabledModels.includes(r.rtn))) || + (r.rtn === tn && (!enabledModels || enabledModels.includes(r.tn))) ); return tableRelations; } - protected getBaseModel(meta): BaseModelSql { this.baseLog(`getBaseModel : '%s'`); this.metas[meta.tn] = meta; - return new BaseModel({ - dbDriver: this.dbDriver, - ...meta, - dbModels: this.models - }, this); + return new BaseModel( + { + dbDriver: this.dbDriver, + ...meta, + dbModels: this.models + }, + this + ); } // NOTE: xc-meta protected async loadHooks(): Promise { this.baseLog(`loadHooks :`); this.hooks = {}; - const hooksList = await this.xcMeta.metaList(this.projectId, this.dbAlias, 'nc_hooks'); + const hooksList = await this.xcMeta.metaList( + this.projectId, + this.dbAlias, + 'nc_hooks' + ); for (const hook of hooksList) { if (!(hook.tn in this.hooks)) { @@ -1220,46 +1499,64 @@ export default abstract class BaseApiBuilder implements XcDynami try { hook.condition = hook.condition && JSON.parse(hook.condition); hook.notification = hook.notification && JSON.parse(hook.notification); - } catch (e) { - } + } catch (e) {} const key = `${hook.event}.${hook.operation}`; this.hooks[hook.tn][key] = this.hooks[hook.tn][key] || []; this.hooks[hook.tn][key].push(hook); } } - // NOTE: xc-meta public async loadFormViews(): Promise { this.baseLog(`loadFormViews :`); this.formViews = {}; - const formViewList = await this.xcMeta.metaList(this.projectId, this.dbAlias, 'nc_models', { - condition: { - show_as: 'form' + const formViewList = await this.xcMeta.metaList( + this.projectId, + this.dbAlias, + 'nc_models', + { + condition: { + show_as: 'form' + } } - }); + ); for (const formView of formViewList) { if (!(formView.parent_model_title in this.formViews)) { this.formViews[formView.parent_model_title] = {}; } try { - formView.query_params = formView.query_params && JSON.parse(formView.query_params); - } catch (e) { - } + formView.query_params = + formView.query_params && JSON.parse(formView.query_params); + } catch (e) {} this.formViews[formView.parent_model_title][formView.id] = formView; } } - protected async generateAndSaveAcl(name: string, type = 'table'): Promise { + protected async generateAndSaveAcl( + name: string, + type = 'table' + ): Promise { this.baseLog(`generateAndSaveAcl : '%s' %s`, name, type); if (type === 'procedure' || type === 'function') { - this.baseLog(`generateAndSaveAcl : Generating and inserting '%s' %s acl`, name, type); - - this.procedureOrFunctionAcls[name] = {creator: true, editor: true, guest: false}; + this.baseLog( + `generateAndSaveAcl : Generating and inserting '%s' %s acl`, + name, + type + ); + + this.procedureOrFunctionAcls[name] = { + creator: true, + editor: true, + guest: false + }; /* create nc_models and its rows if it doesn't exists */ - if (!(await this.xcMeta.metaGet(this.projectId, this.dbAlias, 'nc_acl', {'tn': name}))) { + if ( + !(await this.xcMeta.metaGet(this.projectId, this.dbAlias, 'nc_acl', { + tn: name + })) + ) { await this.xcMeta.metaInsert(this.projectId, this.dbAlias, 'nc_acl', { tn: name, acl: JSON.stringify(this.procedureOrFunctionAcls[name]), @@ -1267,25 +1564,40 @@ export default abstract class BaseApiBuilder implements XcDynami }); } } else { - this.baseLog(`generateAndSaveAcl : Generating and inserting '%s' %s acl`, name, type); - this.acls[name] = new ExpressXcPolicy({dir: '', ctx: {type}, filename: ''}).getObject(); + this.baseLog( + `generateAndSaveAcl : Generating and inserting '%s' %s acl`, + name, + type + ); + this.acls[name] = new ExpressXcPolicy({ + dir: '', + ctx: { type }, + filename: '' + }).getObject(); /* create nc_models and its rows if it doesn't exists */ - if (!(await this.xcMeta.metaGet(this.projectId, this.dbAlias, 'nc_acl', {'tn': name}))) { + if ( + !(await this.xcMeta.metaGet(this.projectId, this.dbAlias, 'nc_acl', { + tn: name + })) + ) { await this.xcMeta.metaInsert(this.projectId, this.dbAlias, 'nc_acl', { tn: name, acl: JSON.stringify(this.acls[name]), type - }) + }); } } } // NOTE: xc-meta protected async readXcModelsAndGroupByType() { - this.baseLog(`readXcModelsAndGroupByType : `); - const metaArr = (await this.xcMeta.metaList(this.projectId, this.dbAlias, 'nc_models')) + const metaArr = await this.xcMeta.metaList( + this.projectId, + this.dbAlias, + 'nc_models' + ); const enabledModels = []; @@ -1299,10 +1611,11 @@ export default abstract class BaseApiBuilder implements XcDynami this.viewsCount = 0; this.relationsCount = 0; - this.baseLog(`readXcModelsAndGroupByType : Iterating and grouping enabled models`) + this.baseLog( + `readXcModelsAndGroupByType : Iterating and grouping enabled models` + ); for (const obj of metaArr) { - if (obj.type === 'procedure') { if (obj.enabled) { procedureArr.push(JSON.parse(obj.meta)); @@ -1326,80 +1639,115 @@ export default abstract class BaseApiBuilder implements XcDynami } } } - return {metaArr, enabledModels, tableAndViewArr, functionArr, procedureArr}; + return { + metaArr, + enabledModels, + tableAndViewArr, + functionArr, + procedureArr + }; } - protected async relationsSyncAndGet(): Promise { - // check if relations already synced - let relations = await this.xcMeta.metaList(this.projectId, this.dbAlias, 'nc_relations', { - fields: [ - 'ur', - 'tn', - '_tn', - 'cn', - '_cn', - 'rtn', - 'rcn', - '_rcn', - '_rtn', - 'type', - 'db_type', - 'dr', - 'fkn' - ] - }); + let relations = await this.xcMeta.metaList( + this.projectId, + this.dbAlias, + 'nc_relations', + { + fields: [ + 'ur', + 'tn', + '_tn', + 'cn', + '_cn', + 'rtn', + 'rcn', + '_rcn', + '_rtn', + 'type', + 'db_type', + 'dr', + 'fkn' + ] + } + ); // check if relations already synced if (relations.length) { this.relationsCount = relations.length; - return relations + return relations; } relations = (await this.sqlClient.relationListAll())?.data?.list; this.relationsCount = relations.length; // check if relations already synced - if ((await this.xcMeta.metaList(this.projectId, this.dbAlias, 'nc_relations')).length) { - return relations + if ( + (await this.xcMeta.metaList(this.projectId, this.dbAlias, 'nc_relations')) + .length + ) { + return relations; } // todo: insert parallelly for (const relation of relations) { relation.enabled = true; relation.fkn = relation?.cstn; - await this.xcMeta.metaInsert(this.projectId, this.dbAlias, 'nc_relations', { - tn: relation.tn, - _tn: this.getTableNameAlias(relation.tn), - cn: relation.cn, - _cn: this.getColumnNameAlias({cn: relation.cn}, relation.tn), - rtn: relation.rtn, - _rtn: this.getTableNameAlias(relation.rtn), - rcn: relation.rcn, - _rcn: this.getColumnNameAlias({cn: relation.rcn}, relation.rtn), - type: 'real', - db_type: this.connectionConfig?.client, - dr: relation?.dr, - ur: relation?.ur, - fkn: relation?.cstn - }) + await this.xcMeta.metaInsert( + this.projectId, + this.dbAlias, + 'nc_relations', + { + tn: relation.tn, + _tn: this.getTableNameAlias(relation.tn), + cn: relation.cn, + _cn: this.getColumnNameAlias({ cn: relation.cn }, relation.tn), + rtn: relation.rtn, + _rtn: this.getTableNameAlias(relation.rtn), + rcn: relation.rcn, + _rcn: this.getColumnNameAlias({ cn: relation.rcn }, relation.rtn), + type: 'real', + db_type: this.connectionConfig?.client, + dr: relation?.dr, + ur: relation?.ur, + fkn: relation?.cstn + } + ); } return relations; } - protected async renameTableNameInXcRelations(oldTableName: string, newTableName: string): Promise { - await this.xcMeta.metaUpdate(this.projectId, this.dbAlias, 'nc_relations', { - tn: newTableName - }, {tn: oldTableName}); + protected async renameTableNameInXcRelations( + oldTableName: string, + newTableName: string + ): Promise { + await this.xcMeta.metaUpdate( + this.projectId, + this.dbAlias, + 'nc_relations', + { + tn: newTableName + }, + { tn: oldTableName } + ); - await this.xcMeta.metaUpdate(this.projectId, this.dbAlias, 'nc_relations', { - rtn: newTableName - }, {rtn: oldTableName}); + await this.xcMeta.metaUpdate( + this.projectId, + this.dbAlias, + 'nc_relations', + { + rtn: newTableName + }, + { rtn: oldTableName } + ); } - - protected async getManyToManyRelations({parent = null, child = null, localMetas = null} = {}): Promise> { + protected async getManyToManyRelations({ + parent = null, + child = null, + localMetas = null + } = {}): Promise> { const metas = new Set(); const assocMetas = new Set(); @@ -1410,12 +1758,17 @@ export default abstract class BaseApiBuilder implements XcDynami } for (const meta of Object.values(this.metas)) { - // check if table is a Bridge table(or Associative Table) by checking // number of foreign keys and columns if (meta.belongsTo?.length === 2 && meta.columns.length < 5) { - - if (parent && child && !([parent, child].includes(meta.belongsTo[0].rtn) && [parent, child].includes(meta.belongsTo[1].rtn))) { + if ( + parent && + child && + !( + [parent, child].includes(meta.belongsTo[0].rtn) && + [parent, child].includes(meta.belongsTo[1].rtn) + ) + ) { continue; } @@ -1426,164 +1779,231 @@ export default abstract class BaseApiBuilder implements XcDynami tableMetaA.hasMany.splice(tableMetaA.hasMany.findIndex(hm => hm.tn === meta.tn), 1) tableMetaB.hasMany.splice(tableMetaB.hasMany.findIndex(hm => hm.tn === meta.tn), 1)*/ - // add manytomany data under metadata of both linked tables tableMetaA.manyToMany = tableMetaA.manyToMany || []; if (tableMetaA.manyToMany.every(mm => mm.vtn !== meta.tn)) { tableMetaA.manyToMany.push({ - "tn": tableMetaA.tn, - "cn": meta.belongsTo[0].rcn, - "vtn": meta.tn, - "vcn": meta.belongsTo[0].cn, - "vrcn": meta.belongsTo[1].cn, - "rtn": meta.belongsTo[1].rtn, - "rcn": meta.belongsTo[1].rcn, - "_tn": tableMetaA._tn, - "_cn": meta.belongsTo[0]._rcn, - "_rtn": meta.belongsTo[1]._rtn, - "_rcn": meta.belongsTo[1]._rcn - }) - metas.add(tableMetaA) + tn: tableMetaA.tn, + cn: meta.belongsTo[0].rcn, + vtn: meta.tn, + vcn: meta.belongsTo[0].cn, + vrcn: meta.belongsTo[1].cn, + rtn: meta.belongsTo[1].rtn, + rcn: meta.belongsTo[1].rcn, + _tn: tableMetaA._tn, + _cn: meta.belongsTo[0]._rcn, + _rtn: meta.belongsTo[1]._rtn, + _rcn: meta.belongsTo[1]._rcn + }); + metas.add(tableMetaA); } // ignore if A & B are same table if (tableMetaB !== tableMetaA) { tableMetaB.manyToMany = tableMetaB.manyToMany || []; if (tableMetaB.manyToMany.every(mm => mm.vtn !== meta.tn)) { tableMetaB.manyToMany.push({ - "tn": tableMetaB.tn, - "cn": meta.belongsTo[1].rcn, - "vtn": meta.tn, - "vcn": meta.belongsTo[1].cn, - "vrcn": meta.belongsTo[0].cn, - "rtn": meta.belongsTo[0].rtn, - "rcn": meta.belongsTo[0].rcn, - "_tn": tableMetaB._tn, - "_cn": meta.belongsTo[1]._rcn, - "_rtn": meta.belongsTo[0]._rtn, - "_rcn": meta.belongsTo[0]._rcn - }) - metas.add(tableMetaB) + tn: tableMetaB.tn, + cn: meta.belongsTo[1].rcn, + vtn: meta.tn, + vcn: meta.belongsTo[1].cn, + vrcn: meta.belongsTo[0].cn, + rtn: meta.belongsTo[0].rtn, + rcn: meta.belongsTo[0].rcn, + _tn: tableMetaB._tn, + _cn: meta.belongsTo[1]._rcn, + _rtn: meta.belongsTo[0]._rtn, + _rcn: meta.belongsTo[0]._rcn + }); + metas.add(tableMetaB); } } - assocMetas.add(meta) + assocMetas.add(meta); } } // Update metadata of tables which have manytomany relation // and recreate basemodel with new meta information for (const meta of metas) { - let queryParams; // update showfields on new many to many relation create if (parent && child) { try { - queryParams = JSON.parse((await this.xcMeta.metaGet(this.projectId, this.dbAlias, 'nc_models', {title: meta.tn})).query_params) + queryParams = JSON.parse( + ( + await this.xcMeta.metaGet( + this.projectId, + this.dbAlias, + 'nc_models', + { title: meta.tn } + ) + ).query_params + ); } catch (e) { // ignore } } meta.v = [ - ...meta.v.filter(vc => !(vc.hm && meta.manyToMany.some(mm => vc.hm.tn === mm.vtn))), + ...meta.v.filter( + vc => !(vc.hm && meta.manyToMany.some(mm => vc.hm.tn === mm.vtn)) + ), // todo: ignore duplicate m2m relations // todo: optimize, just compare associative table(Vtn) - ...meta.manyToMany.filter((v, i) => !meta.v.some(v1 => v1.mm - && ( - v1.mm.tn === v.tn && v.rtn === v1.mm.rtn - || v1.mm.rtn === v.tn && v.tn === v1.mm.rtn - ) && v.vtn === v1.mm.vtn) - // ignore duplicate - && !meta.manyToMany.some((v1, i1) => i1 !== i && v1.tn === v.tn && v.rtn === v1.rtn && v.vtn === v1.vtn) - ).map(mm => { - if (queryParams?.showFields && !(`${mm._tn} <=> ${mm._rtn}` in queryParams.showFields)) { - queryParams.showFields[`${mm._tn} <=> ${mm._rtn}`] = true; - } - - return { - mm, - _cn: `${mm._tn} <=> ${mm._rtn}` - } + ...meta.manyToMany + .filter( + (v, i) => + !meta.v.some( + v1 => + v1.mm && + ((v1.mm.tn === v.tn && v.rtn === v1.mm.rtn) || + (v1.mm.rtn === v.tn && v.tn === v1.mm.rtn)) && + v.vtn === v1.mm.vtn + ) && + // ignore duplicate + !meta.manyToMany.some( + (v1, i1) => + i1 !== i && + v1.tn === v.tn && + v.rtn === v1.rtn && + v.vtn === v1.vtn + ) + ) + .map(mm => { + if ( + queryParams?.showFields && + !(`${mm._tn} <=> ${mm._rtn}` in queryParams.showFields) + ) { + queryParams.showFields[`${mm._tn} <=> ${mm._rtn}`] = true; + } - })] - await this.xcMeta.metaUpdate(this.projectId, this.dbAlias, 'nc_models', { - meta: JSON.stringify(meta), - ...(queryParams ? {query_params: JSON.stringify(queryParams)} : {}) - }, {title: meta.tn}) + return { + mm, + _cn: `${mm._tn} <=> ${mm._rtn}` + }; + }) + ]; + await this.xcMeta.metaUpdate( + this.projectId, + this.dbAlias, + 'nc_models', + { + meta: JSON.stringify(meta), + ...(queryParams ? { query_params: JSON.stringify(queryParams) } : {}) + }, + { title: meta.tn } + ); XcCache.del([this.projectId, this.dbAlias, 'table', meta.tn].join('::')); if (!localMetas) { - this.models[meta.tn] = this.getBaseModel(meta) + this.models[meta.tn] = this.getBaseModel(meta); } } // Update metadata of associative table for (const meta of assocMetas) { - await this.xcMeta.metaUpdate(this.projectId, this.dbAlias, 'nc_models', { - mm: 1, - }, {title: meta.tn}) + await this.xcMeta.metaUpdate( + this.projectId, + this.dbAlias, + 'nc_models', + { + mm: 1 + }, + { title: meta.tn } + ); if (!localMetas) { - XcCache.del([this.projectId, this.dbAlias, 'table', meta.tn].join('::')); - this.models[meta.tn] = this.getBaseModel(meta) + XcCache.del( + [this.projectId, this.dbAlias, 'table', meta.tn].join('::') + ); + this.models[meta.tn] = this.getBaseModel(meta); } } return metas; } - protected async ncUpManyToMany(): Promise { - const models = await this.xcMeta.metaList(this.projectId, this.dbAlias, 'nc_models', { - fields: ['meta'], - condition: { - type: 'table' + const models = await this.xcMeta.metaList( + this.projectId, + this.dbAlias, + 'nc_models', + { + fields: ['meta'], + condition: { + type: 'table' + } } - }); + ); if (!models.length) { - return + return; } const metas = []; // add virtual columns for relations for (const metaObj of models) { const meta = JSON.parse(metaObj.meta); metas.push(meta); - const ctx = this.generateContextForTable(meta.tn, meta.columns, [], meta.hasMany, meta.belongsTo, meta.type, meta._tn); + const ctx = this.generateContextForTable( + meta.tn, + meta.columns, + [], + meta.hasMany, + meta.belongsTo, + meta.type, + meta._tn + ); // generate virtual columns - meta.v = ModelXcMetaFactory.create(this.connectionConfig, {dir: '', ctx, filename: ''}).getVitualColumns(); + meta.v = ModelXcMetaFactory.create(this.connectionConfig, { + dir: '', + ctx, + filename: '' + }).getVitualColumns(); // set default primary values - ModelXcMetaFactory.create(this.connectionConfig, {}).mapDefaultPrimaryValue(meta.columns); + ModelXcMetaFactory.create( + this.connectionConfig, + {} + ).mapDefaultPrimaryValue(meta.columns); // update meta - await this.xcMeta.metaUpdate(this.projectId, this.dbAlias, 'nc_models', { - meta: JSON.stringify(meta) - }, {title: meta.tn}) + await this.xcMeta.metaUpdate( + this.projectId, + this.dbAlias, + 'nc_models', + { + meta: JSON.stringify(meta) + }, + { title: meta.tn } + ); } // generate many to many relations an columns - await this.getManyToManyRelations({localMetas: metas}); + await this.getManyToManyRelations({ localMetas: metas }); return metas; } - private getColumnNameAlias(col, tableName?: string) { - return this.metas?.[tableName]?.columns?.find(c => c.cn === col.cn)?._cn || col._cn || this.getInflectedName(col.cn, this.connectionConfig?.meta?.inflection?.cn); + return ( + this.metas?.[tableName]?.columns?.find(c => c.cn === col.cn)?._cn || + col._cn || + this.getInflectedName(col.cn, this.connectionConfig?.meta?.inflection?.cn) + ); } private baseLog(str, ...args): void { log(`${this.dbAlias} : ${str}`, ...args); } - - private async modifyTableNameInACL(oldName: string, newName: string): Promise { + private async modifyTableNameInACL( + oldName: string, + newName: string + ): Promise { if (oldName === newName) { return; } - - const replaceTableName = (obj) => { + const replaceTableName = obj => { if (!obj || typeof obj !== 'object' || Array.isArray(obj)) { return; } - for (const [key, value] of (Object.entries(obj) as any)) { + for (const [key, value] of Object.entries(obj) as any) { if (!value || typeof value !== 'object') { continue; } @@ -1593,19 +2013,23 @@ export default abstract class BaseApiBuilder implements XcDynami obj[newName] = value; } - replaceTableName(value) + replaceTableName(value); } } }; - - const acls = await this.xcMeta.metaList(this.projectId, this.dbAlias, 'nc_acl', { - xcCondition: { - acl: { - like: '%"custom":%' + const acls = await this.xcMeta.metaList( + this.projectId, + this.dbAlias, + 'nc_acl', + { + xcCondition: { + acl: { + like: '%"custom":%' + } } } - }) + ); for (const aclRow of acls) { const aclObj = JSON.parse(aclRow.acl); @@ -1614,36 +2038,39 @@ export default abstract class BaseApiBuilder implements XcDynami continue; } - for (const acl of (Object.values(aclOps) as any[])) { + for (const acl of Object.values(aclOps) as any[]) { if (typeof acl === 'boolean') { continue; } if ('custom' in acl) { - replaceTableName(acl.custom) + replaceTableName(acl.custom); } } } - await this.xcMeta.metaUpdate(this.projectId, this.dbAlias, 'nc_acl', { - acl: JSON.stringify(aclObj) - }, { - id: aclRow.id - }); - + await this.xcMeta.metaUpdate( + this.projectId, + this.dbAlias, + 'nc_acl', + { + acl: JSON.stringify(aclObj) + }, + { + id: aclRow.id + } + ); } - - } // @ts-ignore private async deleteTableNameInACL(table: string) { - const replaceTableName = (obj) => { + const replaceTableName = obj => { if (!obj || typeof obj !== 'object' || Array.isArray(obj)) { return; } - for (const [key, value] of (Object.entries(obj) as any)) { + for (const [key, value] of Object.entries(obj) as any) { if (!value || typeof value !== 'object') { continue; } @@ -1651,19 +2078,23 @@ export default abstract class BaseApiBuilder implements XcDynami if (key === table) { delete obj[key]; } - replaceTableName(value) + replaceTableName(value); } } }; - - const acls = await this.xcMeta.metaList(this.projectId, this.dbAlias, 'nc_acl', { - xcCondition: { - acl: { - like: '%"custom":%' + const acls = await this.xcMeta.metaList( + this.projectId, + this.dbAlias, + 'nc_acl', + { + xcCondition: { + acl: { + like: '%"custom":%' + } } } - }) + ); for (const aclRow of acls) { const aclObj = JSON.parse(aclRow.acl); @@ -1671,63 +2102,73 @@ export default abstract class BaseApiBuilder implements XcDynami if (typeof aclOps === 'boolean') { continue; } - for (const acl of (Object.values(aclOps) as any[])) { + for (const acl of Object.values(aclOps) as any[]) { if (typeof acl === 'boolean') { continue; } if ('custom' in acl) { - replaceTableName(acl.custom) + replaceTableName(acl.custom); } } } - await this.xcMeta.metaUpdate(this.projectId, this.dbAlias, 'nc_acl', { - acl: JSON.stringify(aclObj) - }, { - id: aclRow.id - }); + await this.xcMeta.metaUpdate( + this.projectId, + this.dbAlias, + 'nc_acl', + { + acl: JSON.stringify(aclObj) + }, + { + id: aclRow.id + } + ); } } - - private async modifyColumnNameInACL(table: string, oldName: string, newName: string): Promise { - this.baseLog(`modifyColumnNameInACL : '${oldName}' => '${newName}', table : '${table}'`) + private async modifyColumnNameInACL( + table: string, + oldName: string, + newName: string + ): Promise { + this.baseLog( + `modifyColumnNameInACL : '${oldName}' => '${newName}', table : '${table}'` + ); if (oldName === newName) { return; } - const findColumnAndRename = (obj) => { + const findColumnAndRename = obj => { if (!obj) { return; } if ('_and' in obj) { for (const o of obj._and) { - findColumnAndRename(o) + findColumnAndRename(o); } } if ('_or' in obj) { for (const o of obj._or) { - findColumnAndRename(o) + findColumnAndRename(o); } } if ('_not' in obj) { - findColumnAndRename(obj._not) + findColumnAndRename(obj._not); } if (oldName in obj) { obj[newName] = obj[oldName]; delete obj[oldName]; } - } - - const replaceColumnName = (obj) => { + }; + const replaceColumnName = obj => { if (!obj || typeof obj !== 'object') { return; } - for (const [key, value] of (Object.entries(obj) as any)) { + for (const [key, value] of Object.entries(obj) as any) { if (!value || typeof value !== 'object') { continue; } @@ -1736,30 +2177,32 @@ export default abstract class BaseApiBuilder implements XcDynami findColumnAndRename(value); } } - replaceColumnName(value) + replaceColumnName(value); } }; - - const acls = await this.xcMeta.metaList(this.projectId, this.dbAlias, 'nc_acl', { - xcCondition: { - acl: { - like: '%"custom":%' + const acls = await this.xcMeta.metaList( + this.projectId, + this.dbAlias, + 'nc_acl', + { + xcCondition: { + acl: { + like: '%"custom":%' + } } } - }); + ); for (const aclRow of acls) { try { const aclObj = JSON.parse(aclRow.acl); for (const aclOps of Object.values(aclObj)) { - if (typeof aclOps === 'boolean') { continue; } - for (const acl of (Object.values(aclOps) as any[])) { - + for (const acl of Object.values(aclOps) as any[]) { if (typeof acl === 'boolean') { continue; } @@ -1768,16 +2211,20 @@ export default abstract class BaseApiBuilder implements XcDynami replaceColumnName(acl.custom); findColumnAndRename(acl.custom); } - } } - - await this.xcMeta.metaUpdate(this.projectId, this.dbAlias, 'nc_acl', { - acl: JSON.stringify(aclObj) - }, { - id: aclRow.id - }); + await this.xcMeta.metaUpdate( + this.projectId, + this.dbAlias, + 'nc_acl', + { + acl: JSON.stringify(aclObj) + }, + { + id: aclRow.id + } + ); } catch (e) { console.log('modifyColumnNameInACL : error : ', e); } @@ -1786,39 +2233,41 @@ export default abstract class BaseApiBuilder implements XcDynami await this.loadXcAcl(); } - private async deleteColumnNameInACL(table: string, cn: string): Promise { - this.baseLog(`deleteColumnNameInACL : '${cn}' in '${table}'`) + private async deleteColumnNameInACL( + table: string, + cn: string + ): Promise { + this.baseLog(`deleteColumnNameInACL : '${cn}' in '${table}'`); - const findColumnAndRename = (obj) => { + const findColumnAndRename = obj => { if (!obj) { return; } if ('_and' in obj) { for (const o of obj._and) { - findColumnAndRename(o) + findColumnAndRename(o); } } if ('_or' in obj) { for (const o of obj._or) { - findColumnAndRename(o) + findColumnAndRename(o); } } if ('_not' in obj) { - findColumnAndRename(obj._not) + findColumnAndRename(obj._not); } if (cn in obj) { delete obj[cn]; } - } - - const replaceColumnName = (obj) => { + }; + const replaceColumnName = obj => { if (!obj || typeof obj !== 'object') { return; } - for (const [key, value] of (Object.entries(obj) as any)) { + for (const [key, value] of Object.entries(obj) as any) { if (!value || typeof value !== 'object') { continue; } @@ -1827,30 +2276,32 @@ export default abstract class BaseApiBuilder implements XcDynami findColumnAndRename(value); } } - replaceColumnName(value) + replaceColumnName(value); } }; - - const acls = await this.xcMeta.metaList(this.projectId, this.dbAlias, 'nc_acl', { - xcCondition: { - acl: { - like: '%"custom":%' + const acls = await this.xcMeta.metaList( + this.projectId, + this.dbAlias, + 'nc_acl', + { + xcCondition: { + acl: { + like: '%"custom":%' + } } } - }); + ); for (const aclRow of acls) { try { const aclObj = JSON.parse(aclRow.acl); for (const aclOps of Object.values(aclObj)) { - if (typeof aclOps === 'boolean') { continue; } - for (const acl of (Object.values(aclOps) as any[])) { - + for (const acl of Object.values(aclOps) as any[]) { if (typeof acl === 'boolean') { continue; } @@ -1859,72 +2310,91 @@ export default abstract class BaseApiBuilder implements XcDynami replaceColumnName(acl.custom); findColumnAndRename(acl.custom); } - } } - - await this.xcMeta.metaUpdate(this.projectId, this.dbAlias, 'nc_acl', { - acl: JSON.stringify(aclObj) - }, { - id: aclRow.id - }); + await this.xcMeta.metaUpdate( + this.projectId, + this.dbAlias, + 'nc_acl', + { + acl: JSON.stringify(aclObj) + }, + { + id: aclRow.id + } + ); } catch (e) { console.log('modifyColumnNameInACL : error : ', e); } } - await this.loadXcAcl() + await this.loadXcAcl(); } - private async deleteRelationInACL(parentTable: string, childTable: string): Promise { - - this.baseLog(`deleteRelationInACL : '${parentTable}' => '${childTable}'`) + private async deleteRelationInACL( + parentTable: string, + childTable: string + ): Promise { + this.baseLog(`deleteRelationInACL : '${parentTable}' => '${childTable}'`); const relationExist = (ancestor, obj, exist = false) => { - if (!obj || typeof obj !== 'object') { return exist; } - for (const [key, value] of (Object.entries(obj) as any)) { + for (const [key, value] of Object.entries(obj) as any) { if (!value || typeof value !== 'object') { continue; } if ('relationType' in value) { - if (ancestor === parentTable && key === childTable && value.relationType === 'hm') { - return true + if ( + ancestor === parentTable && + key === childTable && + value.relationType === 'hm' + ) { + return true; } - if (ancestor === childTable && key === parentTable && value.relationType === 'bt') { + if ( + ancestor === childTable && + key === parentTable && + value.relationType === 'bt' + ) { return true; } - return exist || relationExist(value.relationType ? key : ancestor, value, exist); + return ( + exist || + relationExist(value.relationType ? key : ancestor, value, exist) + ); } else { - return exist || relationExist(ancestor, value, exist) + return exist || relationExist(ancestor, value, exist); } } return exist; - } + }; - const acls = await this.xcMeta.metaList(this.projectId, this.dbAlias, 'nc_acl', { - xcCondition: { - acl: { - like: '%"custom":%' + const acls = await this.xcMeta.metaList( + this.projectId, + this.dbAlias, + 'nc_acl', + { + xcCondition: { + acl: { + like: '%"custom":%' + } } } - }); + ); for (const aclRow of acls) { try { const aclObj = JSON.parse(aclRow.acl); for (const aclOps of Object.values(aclObj)) { - if (typeof aclOps === 'boolean') { continue; } - for (const acl of (Object.values(aclOps) as any[])) { - + for (const acl of Object.values(aclOps) as any[]) { if (typeof acl === 'boolean') { continue; } @@ -1932,44 +2402,50 @@ export default abstract class BaseApiBuilder implements XcDynami if ('custom' in acl && relationExist(aclRow.tn, acl.custom)) { delete acl.custom; } - } } - - await this.xcMeta.metaUpdate(this.projectId, this.dbAlias, 'nc_acl', { - acl: JSON.stringify(aclObj) - }, { - id: aclRow.id - }); + await this.xcMeta.metaUpdate( + this.projectId, + this.dbAlias, + 'nc_acl', + { + acl: JSON.stringify(aclObj) + }, + { + id: aclRow.id + } + ); } catch (e) { console.log('modifyColumnNameInACL : error : ', e); } } - await this.loadXcAcl() + await this.loadXcAcl(); } public async onTableCreate(_tn: string, _args?: any) { - Tele.emit('evt', {evt_type: 'table:created'}) + Tele.emit('evt', { evt_type: 'table:created' }); } public onVirtualTableUpdate(args: any) { - const meta = XcCache.get([this.projectId, this.dbAlias, 'table', args.tn].join('::')); + const meta = XcCache.get( + [this.projectId, this.dbAlias, 'table', args.tn].join('::') + ); if (meta && meta.id === args.id) { XcCache.del([this.projectId, this.dbAlias, 'table', args.tn].join('::')); // todo: update meta and model } if (args?.query_params?.extraViewParams?.formParams) { - this.formViews[args.tn][args.id].query_params = args.query_params + this.formViews[args.tn][args.id].query_params = args.query_params; } } - public getMeta(tableName:string):any{ - return this.metas?.[tableName] + public getMeta(tableName: string): any { + return this.metas?.[tableName]; } } -export {IGNORE_TABLES}; +export { IGNORE_TABLES }; /** * @copyright Copyright (c) 2021, Xgene Cloud Ltd diff --git a/packages/nocodb/src/lib/noco/common/BaseProcedure.ts b/packages/nocodb/src/lib/noco/common/BaseProcedure.ts index 6aa51815d1..311ab5eeee 100644 --- a/packages/nocodb/src/lib/noco/common/BaseProcedure.ts +++ b/packages/nocodb/src/lib/noco/common/BaseProcedure.ts @@ -1,16 +1,14 @@ -import {GqlApiBuilder} from "../gql/GqlApiBuilder"; -import {RestApiBuilder} from "../rest/RestApiBuilder"; +import { GqlApiBuilder } from '../gql/GqlApiBuilder'; +import { RestApiBuilder } from '../rest/RestApiBuilder'; -import XcProcedure from "./XcProcedure"; +import XcProcedure from './XcProcedure'; export default class BaseProcedure { - - protected builder: GqlApiBuilder | RestApiBuilder; + protected builder: GqlApiBuilder | RestApiBuilder; protected procedures: any[]; - protected functions: any[] + protected functions: any[]; protected xcProcedure: XcProcedure; - public functionsSet(functions): void { this.functions = functions; } @@ -30,17 +28,16 @@ export default class BaseProcedure { public functionDelete(name: string): void { const index = this.functions.findIndex(f => f.function_name === name); if (index > -1) { - this.functions.splice(index, 1) + this.functions.splice(index, 1); } } public procedureDelete(name: string): void { const index = this.procedures.findIndex(f => f.procedure_name === name); if (index > -1) { - this.procedures.splice(index, 1) + this.procedures.splice(index, 1); } } - } /** diff --git a/packages/nocodb/src/lib/noco/common/NcConnectionMgr.ts b/packages/nocodb/src/lib/noco/common/NcConnectionMgr.ts index 70bc3853e8..e374eb3afd 100644 --- a/packages/nocodb/src/lib/noco/common/NcConnectionMgr.ts +++ b/packages/nocodb/src/lib/noco/common/NcConnectionMgr.ts @@ -1,19 +1,19 @@ -import {XKnex} from "../../dataMapper"; -import {NcConfig} from "../../../interface/config"; -import fs from "fs"; -import Knex from "knex"; +import { XKnex } from '../../dataMapper'; +import { NcConfig } from '../../../interface/config'; +import fs from 'fs'; +import Knex from 'knex'; -import {SqlClientFactory} from 'nc-help'; -import NcMetaIO from "../meta/NcMetaIO"; -import {defaultConnectionConfig} from "../../utils/NcConfigFactory"; +import { SqlClientFactory } from 'nc-help'; +import NcMetaIO from '../meta/NcMetaIO'; +import { defaultConnectionConfig } from '../../utils/NcConfigFactory'; export default class NcConnectionMgr { private static connectionRefs: { [projectId: string]: { [env: string]: { - [dbAlias: string]: XKnex - } - } + [dbAlias: string]: XKnex; + }; + }; } = {}; private static metaKnex: NcMetaIO; @@ -23,13 +23,13 @@ export default class NcConnectionMgr { } public static delete({ - dbAlias = 'db', - env = '_noco', - projectId - }: { - dbAlias: string, - env: string, - projectId: string + dbAlias = 'db', + env = '_noco', + projectId + }: { + dbAlias: string; + env: string; + projectId: string; }) { // todo: ignore meta projects if (this.connectionRefs?.[projectId]?.[env]?.[dbAlias]) { @@ -44,27 +44,31 @@ export default class NcConnectionMgr { } public static get({ - dbAlias = 'db', - env = '_noco', - config, - projectId - }: { - dbAlias: string, - env: string, - config: NcConfig, - projectId: string + dbAlias = 'db', + env = '_noco', + config, + projectId + }: { + dbAlias: string; + env: string; + config: NcConfig; + projectId: string; }): XKnex { if (this.connectionRefs?.[projectId]?.[env]?.[dbAlias]) { return this.connectionRefs?.[projectId]?.[env]?.[dbAlias]; } this.connectionRefs[projectId] = this.connectionRefs[projectId] || {}; - this.connectionRefs[projectId][env] = this.connectionRefs[projectId] [env] || {}; + this.connectionRefs[projectId][env] = + this.connectionRefs[projectId][env] || {}; if (config?.prefix && this.metaKnex) { this.connectionRefs[projectId][env][dbAlias] = this.metaKnex?.knex; } else { - const connectionConfig = this.getConnectionConfig(config, env, dbAlias) + const connectionConfig = this.getConnectionConfig(config, env, dbAlias); - if (connectionConfig?.connection?.ssl && typeof connectionConfig?.connection?.ssl === 'object') { + if ( + connectionConfig?.connection?.ssl && + typeof connectionConfig?.connection?.ssl === 'object' + ) { if (connectionConfig.connection.ssl.caFilePath) { connectionConfig.connection.ssl.ca = fs .readFileSync(connectionConfig.connection.ssl.caFilePath) @@ -85,58 +89,66 @@ export default class NcConnectionMgr { const isSqlite = connectionConfig?.client === 'sqlite3'; if (connectionConfig?.connection?.port) { - connectionConfig.connection.port = +connectionConfig.connection.port + connectionConfig.connection.port = +connectionConfig.connection.port; } - this.connectionRefs[projectId][env][dbAlias] = XKnex(isSqlite ? - connectionConfig.connection as Knex.Config : - { - ...connectionConfig, - connection: { - ...defaultConnectionConfig, - ...connectionConfig.connection, - typeCast(_field, next) { - const res = next(); - if (res instanceof Buffer) { - return [...res].map(v => ('00' + v.toString(16)).slice(-2)).join(''); + this.connectionRefs[projectId][env][dbAlias] = XKnex( + isSqlite + ? (connectionConfig.connection as Knex.Config) + : ({ + ...connectionConfig, + connection: { + ...defaultConnectionConfig, + ...connectionConfig.connection, + typeCast(_field, next) { + const res = next(); + if (res instanceof Buffer) { + return [...res] + .map(v => ('00' + v.toString(16)).slice(-2)) + .join(''); + } + return res; + } } - return res; - } - } - } as any); + } as any) + ); if (isSqlite) { - this.connectionRefs[projectId][env][dbAlias].raw(`PRAGMA journal_mode=WAL;`).then(() => { - }) + this.connectionRefs[projectId][env][dbAlias] + .raw(`PRAGMA journal_mode=WAL;`) + .then(() => {}); } } return this.connectionRefs[projectId][env][dbAlias]; } - - private static getConnectionConfig(config: NcConfig, env: string, dbAlias: string) { + private static getConnectionConfig( + config: NcConfig, + env: string, + dbAlias: string + ) { return config?.envs?.[env]?.db?.find(db => db?.meta?.dbAlias === dbAlias); } public static getSqlClient({ - projectId, - dbAlias = 'db', - env = '_noco', - config - }: { - dbAlias: string, - env: string, - config: NcConfig, - projectId: string + projectId, + dbAlias = 'db', + env = '_noco', + config + }: { + dbAlias: string; + env: string; + config: NcConfig; + projectId: string; }): any { const knex = this.get({ dbAlias, env, config, projectId - }) - return SqlClientFactory.create({knex, ...this.getConnectionConfig(config, env, dbAlias)}) + }); + return SqlClientFactory.create({ + knex, + ...this.getConnectionConfig(config, env, dbAlias) + }); } } - - - diff --git a/packages/nocodb/src/lib/noco/common/XcAudit.ts b/packages/nocodb/src/lib/noco/common/XcAudit.ts index ec5c0a4fb2..bf7fb1c5f2 100644 --- a/packages/nocodb/src/lib/noco/common/XcAudit.ts +++ b/packages/nocodb/src/lib/noco/common/XcAudit.ts @@ -1,20 +1,13 @@ -import Noco from "../Noco"; +import Noco from '../Noco'; -export default class XcAudit{ - - public static init(app:Noco){ +export default class XcAudit { + public static init(app: Noco) { this.app = app; } // @ts-ignore - private static app:Noco; + private static app: Noco; // @ts-ignore - public static async log(data:{ - project - }){ - - } - - -} \ No newline at end of file + public static async log(data: { project }) {} +} diff --git a/packages/nocodb/src/lib/noco/common/XcCron.ts b/packages/nocodb/src/lib/noco/common/XcCron.ts index 6d01598a0f..c93ac0177a 100644 --- a/packages/nocodb/src/lib/noco/common/XcCron.ts +++ b/packages/nocodb/src/lib/noco/common/XcCron.ts @@ -1,15 +1,13 @@ -import {CronJob} from 'cron'; +import { CronJob } from 'cron'; -import {NcConfig} from "../../../interface/config"; -import Noco from "../Noco"; +import { NcConfig } from '../../../interface/config'; +import Noco from '../Noco'; -import BaseApiBuilder from "./BaseApiBuilder"; +import BaseApiBuilder from './BaseApiBuilder'; // import * as tsc from "typescript"; - export class XcCron { - // @ts-ignore private app: Noco; // @ts-ignore @@ -17,7 +15,6 @@ export class XcCron { private apiBuilder: BaseApiBuilder; private cronJobs: { [key: string]: CronJob }; - constructor(config: NcConfig, apiBuilder: BaseApiBuilder, app: Noco) { this.app = app; this.config = config; @@ -27,17 +24,20 @@ export class XcCron { public async init(): Promise { // const cronJobs = await this.apiBuilder.getDbDriver()('nc_cron').select(); - const cronJobs = await this.apiBuilder.getXcMeta().metaList('', this.apiBuilder.dbAlias, 'nc_cron'); + const cronJobs = await this.apiBuilder + .getXcMeta() + .metaList('', this.apiBuilder.dbAlias, 'nc_cron'); for (const cron of cronJobs) { this.startCronJob(cron); } - } public async restartCron(args: any): Promise { // const cron = await this.apiBuilder.getDbDriver()('nc_cron').where('title', args.title).first(); - const cron = await this.apiBuilder.getXcMeta().metaGet('', this.apiBuilder.dbAlias, 'nc_cron', {title: args.title}); + const cron = await this.apiBuilder + .getXcMeta() + .metaGet('', this.apiBuilder.dbAlias, 'nc_cron', { title: args.title }); if (cron.id in this.cronJobs) { this.cronJobs[cron.id].stop(); @@ -52,10 +52,9 @@ export class XcCron { } } - private startCronJob(cron): void { if (!cron.active) { - return + return; } try { const job = new CronJob( @@ -68,13 +67,11 @@ export class XcCron { job.start(); this.cronJobs[cron.id] = job; } catch (e) { - console.log('Error in cron initialization : ', e.message) + console.log('Error in cron initialization : ', e.message); } } - private generateCronHandlerFromStringBody(fnBody: string): any { - // @ts-ignore let handler = () => { console.log('Empty handler'); @@ -95,16 +92,16 @@ export class XcCron { // tslint:disable-next-line:no-eval handler = eval(js); // console.timeEnd('startTrans') - } catch (e) { - console.log('Error in Cron handler transpilation', e) + console.log('Error in Cron handler transpilation', e); } // tslint:disable-next-line:no-eval } return handler; } -}/** +} +/** * @copyright Copyright (c) 2021, Xgene Cloud Ltd * * @author Naveen MR diff --git a/packages/nocodb/src/lib/noco/common/XcProcedure.ts b/packages/nocodb/src/lib/noco/common/XcProcedure.ts index 8a0f174575..3076510637 100644 --- a/packages/nocodb/src/lib/noco/common/XcProcedure.ts +++ b/packages/nocodb/src/lib/noco/common/XcProcedure.ts @@ -1,4 +1,4 @@ -import BaseApiBuilder from "./BaseApiBuilder"; +import BaseApiBuilder from './BaseApiBuilder'; export default class XcProcedure { private builder: BaseApiBuilder; @@ -10,10 +10,22 @@ export default class XcProcedure { public async callFunction(name: string, args: any[]) { try { if (this.builder.getDbType() === 'mssql') { - const result = await this.builder.getDbDriver().raw(`select dbo.??(${new Array(args.length).fill('?').join(',')}) as ??`, [name, ...args, name]); + const result = await this.builder + .getDbDriver() + .raw( + `select dbo.??(${new Array(args.length) + .fill('?') + .join(',')}) as ??`, + [name, ...args, name] + ); return result[0]; } else { - const result = await this.builder.getDbDriver().raw(`select ??(${new Array(args.length).fill('?').join(',')}) as ??`, [name, ...args, name]); + const result = await this.builder + .getDbDriver() + .raw( + `select ??(${new Array(args.length).fill('?').join(',')}) as ??`, + [name, ...args, name] + ); return result[0]; } } catch (e) { @@ -24,7 +36,7 @@ export default class XcProcedure { public async callProcedure(name: string, args: any[]) { try { if (this.builder.getDbType() === 'mssql') { - throw new Error('Not implemented') + throw new Error('Not implemented'); /* const sql = require('mssql'); const request = new sql.Request({ @@ -47,22 +59,39 @@ export default class XcProcedure { // const result = '' // mcnd await this.builder.getDbDriver().raw(`Call ??(${Array.from({length: count}, (_, i) => '@var' + i).join(',')})`, [name]); // // return result) - } else if (this.builder.getDbType() === 'mysql2' || this.builder.getDbType() === 'mysql') { - const knexRef = args.reduce((knex, val, i) => knex.raw(`SET @var${i}=?`, [val]), this.builder.getDbDriver().schema) - const count = args.length - const result = await knexRef.raw(`Call ??(${Array.from({length: count}, (_, i) => '@var' + i).join(',')})`, [name]); + } else if ( + this.builder.getDbType() === 'mysql2' || + this.builder.getDbType() === 'mysql' + ) { + const knexRef = args.reduce( + (knex, val, i) => knex.raw(`SET @var${i}=?`, [val]), + this.builder.getDbDriver().schema + ); + const count = args.length; + const result = await knexRef.raw( + `Call ??(${Array.from({ length: count }, (_, i) => '@var' + i).join( + ',' + )})`, + [name] + ); return [result[count][0][0]]; } else if (this.builder.getDbType() === 'pg') { - const result = await this.builder.getDbDriver().raw(`Call ??(${new Array(args.length).fill('?').join(',')})`, [name, ...args]); + const result = await this.builder + .getDbDriver() + .raw(`Call ??(${new Array(args.length).fill('?').join(',')})`, [ + name, + ...args + ]); return result; } else { - throw new Error('Not implemented') + throw new Error('Not implemented'); } } catch (e) { - throw (e) + throw e; } } -}/** +} +/** * @copyright Copyright (c) 2021, Xgene Cloud Ltd * * @author Naveen MR diff --git a/packages/nocodb/src/lib/noco/common/formSubmissionEmailTemplate.ts b/packages/nocodb/src/lib/noco/common/formSubmissionEmailTemplate.ts index 7023b7f8a2..9d21625a4b 100644 --- a/packages/nocodb/src/lib/noco/common/formSubmissionEmailTemplate.ts +++ b/packages/nocodb/src/lib/noco/common/formSubmissionEmailTemplate.ts @@ -193,7 +193,7 @@ export default ` -` +`; /** * @copyright Copyright (c) 2021, Xgene Cloud Ltd diff --git a/packages/nocodb/src/lib/noco/common/helpers/addErrorOnColumnDeleteInFormula.ts b/packages/nocodb/src/lib/noco/common/helpers/addErrorOnColumnDeleteInFormula.ts index 57cfdd91ed..b65f942f6e 100644 --- a/packages/nocodb/src/lib/noco/common/helpers/addErrorOnColumnDeleteInFormula.ts +++ b/packages/nocodb/src/lib/noco/common/helpers/addErrorOnColumnDeleteInFormula.ts @@ -1,18 +1,19 @@ -export default function (args: { - virtualColumns, - columnName: string +export default function(args: { + virtualColumns; + columnName: string; }): void | boolean { - let modified = false; const fn = (pt, virtualColumn) => { if (pt.type === 'CallExpression') { - pt.arguments.map(arg => fn(arg, virtualColumn)) + pt.arguments.map(arg => fn(arg, virtualColumn)); } else if (pt.type === 'Literal') { } else if (pt.type === 'Identifier') { if (pt.name === args.columnName) { virtualColumn.formula.error = virtualColumn.formula.error || []; - virtualColumn.formula.error.push(`Column '${args.columnName}' was deleted`) + virtualColumn.formula.error.push( + `Column '${args.columnName}' was deleted` + ); modified = true; } } else if (pt.type === 'BinaryExpression') { @@ -22,13 +23,13 @@ export default function (args: { }; if (!args.virtualColumns) { - return + return; } for (const v of args.virtualColumns) { if (!v.formula?.tree) { continue; } - fn(v.formula.tree, v) + fn(v.formula.tree, v); } return modified; -} \ No newline at end of file +} diff --git a/packages/nocodb/src/lib/noco/common/helpers/jsepTreeToFormula.ts b/packages/nocodb/src/lib/noco/common/helpers/jsepTreeToFormula.ts index 75c9ce68dc..74140581a6 100644 --- a/packages/nocodb/src/lib/noco/common/helpers/jsepTreeToFormula.ts +++ b/packages/nocodb/src/lib/noco/common/helpers/jsepTreeToFormula.ts @@ -1,43 +1,67 @@ export default function jsepTreeToFormula(node) { if (node.type === 'BinaryExpression' || node.type === 'LogicalExpression') { - return '(' + jsepTreeToFormula(node.left) + ' ' + node.operator + ' ' + jsepTreeToFormula(node.right) + ')' + return ( + '(' + + jsepTreeToFormula(node.left) + + ' ' + + node.operator + + ' ' + + jsepTreeToFormula(node.right) + + ')' + ); } if (node.type === 'UnaryExpression') { - return node.operator + jsepTreeToFormula(node.argument) + return node.operator + jsepTreeToFormula(node.argument); } if (node.type === 'MemberExpression') { - return jsepTreeToFormula(node.object) + '[' + jsepTreeToFormula(node.property) + ']' + return ( + jsepTreeToFormula(node.object) + + '[' + + jsepTreeToFormula(node.property) + + ']' + ); } if (node.type === 'Identifier') { - return node.name + return node.name; } if (node.type === 'Literal') { if (typeof node.value === 'string') { - return '"' + node.value + '"' + return '"' + node.value + '"'; } - return '' + node.value + return '' + node.value; } if (node.type === 'CallExpression') { - return jsepTreeToFormula(node.callee) + '(' + node.arguments.map(jsepTreeToFormula).join(', ') + ')' + return ( + jsepTreeToFormula(node.callee) + + '(' + + node.arguments.map(jsepTreeToFormula).join(', ') + + ')' + ); } if (node.type === 'ArrayExpression') { - return '[' + node.elements.map(jsepTreeToFormula).join(', ') + ']' + return '[' + node.elements.map(jsepTreeToFormula).join(', ') + ']'; } if (node.type === 'Compound') { - return node.body.map(e => jsepTreeToFormula(e)).join(' ') + return node.body.map(e => jsepTreeToFormula(e)).join(' '); } if (node.type === 'ConditionalExpression') { - return jsepTreeToFormula(node.test) + ' ? ' + jsepTreeToFormula(node.consequent) + ' : ' + jsepTreeToFormula(node.alternate) + return ( + jsepTreeToFormula(node.test) + + ' ? ' + + jsepTreeToFormula(node.consequent) + + ' : ' + + jsepTreeToFormula(node.alternate) + ); } - return '' -} \ No newline at end of file + return ''; +} diff --git a/packages/nocodb/src/lib/noco/common/helpers/updateColumnNameInFormula.ts b/packages/nocodb/src/lib/noco/common/helpers/updateColumnNameInFormula.ts index 53d42f5d24..e729efaf21 100644 --- a/packages/nocodb/src/lib/noco/common/helpers/updateColumnNameInFormula.ts +++ b/packages/nocodb/src/lib/noco/common/helpers/updateColumnNameInFormula.ts @@ -1,16 +1,15 @@ -import jsepTreeToFormula from "./jsepTreeToFormula"; +import jsepTreeToFormula from './jsepTreeToFormula'; -export default function (args: { - virtualColumns, - oldColumnName: string, - newColumnName: string, +export default function(args: { + virtualColumns; + oldColumnName: string; + newColumnName: string; }): void | boolean { - let modified = false; - const fn = (pt) => { + const fn = pt => { if (pt.type === 'CallExpression') { - pt.arguments.map(arg => fn(arg)) + pt.arguments.map(arg => fn(arg)); } else if (pt.type === 'Literal') { } else if (pt.type === 'Identifier') { if (pt.name === args.oldColumnName) { @@ -24,14 +23,14 @@ export default function (args: { }; if (!args.virtualColumns) { - return + return; } for (const v of args.virtualColumns) { if (!v.formula?.tree) { continue; } - fn(v.formula.tree) - v.formula.value = jsepTreeToFormula(v.formula.tree) + fn(v.formula.tree); + v.formula.value = jsepTreeToFormula(v.formula.tree); } return modified; -} \ No newline at end of file +} diff --git a/packages/nocodb/src/lib/noco/gql/GqlApiBuilder.ts b/packages/nocodb/src/lib/noco/gql/GqlApiBuilder.ts index 3858aaacb5..32f3e21e24 100644 --- a/packages/nocodb/src/lib/noco/gql/GqlApiBuilder.ts +++ b/packages/nocodb/src/lib/noco/gql/GqlApiBuilder.ts @@ -1,32 +1,30 @@ -import DataLoader from "dataloader"; +import DataLoader from 'dataloader'; import debug from 'debug'; -import {Router} from "express"; -import {execute} from "graphql"; -import {GraphQLJSON} from 'graphql-type-json'; +import { Router } from 'express'; +import { execute } from 'graphql'; +import { GraphQLJSON } from 'graphql-type-json'; import _ from 'lodash'; -import {BaseType} from 'xc-core-ts'; - -import XcMetaMgr from "../../../interface/XcMetaMgr"; -import {DbConfig, NcConfig} from "../../../interface/config"; -import ExpressXcTsPolicyGql from "../../sqlMgr/code/gql-policies/xc-ts/ExpressXcTsPolicyGql"; -import GqlXcSchemaFactory from "../../sqlMgr/code/gql-schema/xc-ts/GqlXcSchemaFactory"; -import ModelXcMetaFactory from "../../sqlMgr/code/models/xc/ModelXcMetaFactory"; -import NcHelp from "../../utils/NcHelp"; -import NcProjectBuilder from "../NcProjectBuilder"; -import Noco from "../Noco"; -import BaseApiBuilder from "../common/BaseApiBuilder"; -import NcMetaIO from "../meta/NcMetaIO"; - -import {m2mNotChildren, m2mNotChildrenCount} from "./GqlCommonResolvers"; -import GqlMiddleware from "./GqlMiddleware"; -import {GqlProcedureResolver} from "./GqlProcedureResolver"; -import GqlResolver from "./GqlResolver"; +import { BaseType } from 'xc-core-ts'; + +import XcMetaMgr from '../../../interface/XcMetaMgr'; +import { DbConfig, NcConfig } from '../../../interface/config'; +import ExpressXcTsPolicyGql from '../../sqlMgr/code/gql-policies/xc-ts/ExpressXcTsPolicyGql'; +import GqlXcSchemaFactory from '../../sqlMgr/code/gql-schema/xc-ts/GqlXcSchemaFactory'; +import ModelXcMetaFactory from '../../sqlMgr/code/models/xc/ModelXcMetaFactory'; +import NcHelp from '../../utils/NcHelp'; +import NcProjectBuilder from '../NcProjectBuilder'; +import Noco from '../Noco'; +import BaseApiBuilder from '../common/BaseApiBuilder'; +import NcMetaIO from '../meta/NcMetaIO'; + +import { m2mNotChildren, m2mNotChildrenCount } from './GqlCommonResolvers'; +import GqlMiddleware from './GqlMiddleware'; +import { GqlProcedureResolver } from './GqlProcedureResolver'; +import GqlResolver from './GqlResolver'; import commonSchema from './common.schema'; - const log = debug('nc:api:gql'); - const IGNORE_TABLES = [ 'nc_models', 'nc_roles', @@ -59,25 +57,32 @@ class XCType { constructor(o) { for (const k in o) { if (!this[k]) { - this[k] = o[k] + this[k] = o[k]; } } } } - export class GqlApiBuilder extends BaseApiBuilder implements XcMetaMgr { - public readonly type = 'gql'; - private resolvers: { [key: string]: GqlResolver | GqlProcedureResolver, ___procedure?: GqlProcedureResolver }; + private resolvers: { + [key: string]: GqlResolver | GqlProcedureResolver; + ___procedure?: GqlProcedureResolver; + }; private schemas: { [key: string]: any }; - private types: { [key: string]: new(o: any) => any }; + private types: { [key: string]: new (o: any) => any }; private policies: { [key: string]: any }; private readonly gqlRouter: Router; private resolversCount = 0; private customResolver: any; - constructor(app: Noco, projectBuilder: NcProjectBuilder, config: NcConfig, connectionConfig: DbConfig, xcMeta?: NcMetaIO) { + constructor( + app: Noco, + projectBuilder: NcProjectBuilder, + config: NcConfig, + connectionConfig: DbConfig, + xcMeta?: NcMetaIO + ) { super(app, projectBuilder, config, connectionConfig); this.config = config; this.connectionConfig = connectionConfig; @@ -90,58 +95,88 @@ export class GqlApiBuilder extends BaseApiBuilder implements XcMetaMgr { this.xcMeta = xcMeta; } - public async init(): Promise { await super.init(); return await this.loadResolvers(null); } public async onToggleModelRelation(relationInModels: any): Promise { - this.log(`onToggleModelRelation: Within ToggleModelRelation event handler`) + this.log(`onToggleModelRelation: Within ToggleModelRelation event handler`); - const modelNames: string[] = [...new Set(relationInModels.map(rel => { - return rel.relationType === 'hm' ? rel.rtn : rel.tn - }))] as string[]; + const modelNames: string[] = [ + ...new Set( + relationInModels.map(rel => { + return rel.relationType === 'hm' ? rel.rtn : rel.tn; + }) + ) + ] as string[]; // get current meta from db - const metas = await this.xcMeta.metaList(this.projectId, this.dbAlias, 'nc_models', { - xcCondition: { - 'title': { - in: modelNames + const metas = await this.xcMeta.metaList( + this.projectId, + this.dbAlias, + 'nc_models', + { + xcCondition: { + title: { + in: modelNames + } } } - }); + ); - for (const {meta, id, title, + for (const { + meta, + id, + title // schema_previous } of metas) { const metaObj = JSON.parse(meta); /* filter relation where this table is present */ - const hasMany = metaObj.hasMany.filter(({enabled}) => enabled) - const belongsTo = metaObj.belongsTo.filter(({enabled}) => enabled) + const hasMany = metaObj.hasMany.filter(({ enabled }) => enabled); + const belongsTo = metaObj.belongsTo.filter(({ enabled }) => enabled); const columns = await this.getColumnList(title); - const ctx = this.generateContextForTable(title, columns, [...hasMany, ...belongsTo], hasMany, belongsTo); - + const ctx = this.generateContextForTable( + title, + columns, + [...hasMany, ...belongsTo], + hasMany, + belongsTo + ); const oldSchema = this.schemas[title]; - this.log(`onToggleModelRelation : Generating schema for '%s' table`, title) - const newSchema = this.schemas[title] = GqlXcSchemaFactory.create(this.connectionConfig, this.generateRendererArgs(ctx)).getString(); + this.log( + `onToggleModelRelation : Generating schema for '%s' table`, + title + ); + const newSchema = (this.schemas[title] = GqlXcSchemaFactory.create( + this.connectionConfig, + this.generateRendererArgs(ctx) + ).getString()); if (oldSchema !== this.schemas[title]) { // keep upto 5 schema backup on table update // const previousSchemas = [oldSchema] // if (schema_previous) { // previousSchemas = [...JSON.parse(schema_previous), oldSchema].slice(-5); // } - this.log(`onToggleModelRelation : Updating and taking backup of schema for '%s' table`, title) - - await this.xcMeta.metaUpdate(this.projectId, this.dbAlias, 'nc_models', { - schema: newSchema, - // schema_previous: JSON.stringify(previousSchemas) - }, { - id - }); - + this.log( + `onToggleModelRelation : Updating and taking backup of schema for '%s' table`, + title + ); + + await this.xcMeta.metaUpdate( + this.projectId, + this.dbAlias, + 'nc_models', + { + schema: newSchema + // schema_previous: JSON.stringify(previousSchemas) + }, + { + id + } + ); } } await this.xcTablesRead(modelNames); @@ -149,53 +184,56 @@ export class GqlApiBuilder extends BaseApiBuilder implements XcMetaMgr { } public async onTableCreate(tn: string, args): Promise { - this.log(`onTableCreate : '%s' `, tn) + this.log(`onTableCreate : '%s' `, tn); await super.onTableCreate(tn, args); const columns = { - [tn]: args?.columns?.map(({altered: _al, ...rest}) => rest) - } - + [tn]: args?.columns?.map(({ altered: _al, ...rest }) => rest) + }; - await this.xcTablesPopulate({tableNames: [{tn, _tn: args._tn}], columns}); + await this.xcTablesPopulate({ + tableNames: [{ tn, _tn: args._tn }], + columns + }); await this.reInitializeGraphqlEndpoint(); } public async onTableDelete(tn: string): Promise { await super.onTableDelete(tn); - this.log(`onTableDelete : '%s' `, tn) + this.log(`onTableDelete : '%s' `, tn); delete this.models[tn]; - await this.xcTablesRowDelete(tn) + await this.xcTablesRowDelete(tn); delete this.resolvers[tn]; delete this.schemas[tn]; - await this.reInitializeGraphqlEndpoint(); } public async onViewDelete(viewName: string): Promise { - this.log(`onViewDelete : '%s' `, viewName) + this.log(`onViewDelete : '%s' `, viewName); delete this.models[viewName]; - await this.xcTablesRowDelete(viewName) + await this.xcTablesRowDelete(viewName); delete this.resolvers[viewName]; delete this.schemas[viewName]; await this.reInitializeGraphqlEndpoint(); } // todo: m2m - public async onTableRename(oldTableName: string, newTableName: string): Promise { - - this.log(`onTableRename : '%s' => '%s' `, oldTableName, newTableName) + public async onTableRename( + oldTableName: string, + newTableName: string + ): Promise { + this.log(`onTableRename : '%s' => '%s' `, oldTableName, newTableName); // await this.onTableDelete(oldTableName); // await this.onTableCreate(newTableName); await super.onTableRename(oldTableName, newTableName); - await this.xcTableRename(oldTableName, newTableName) + await this.xcTableRename(oldTableName, newTableName); } public async loadResolvers(customResolver: any): Promise { this.customResolver = customResolver; - this.log(`Initializing graphql api`) + this.log(`Initializing graphql api`); const t = process.hrtime(); @@ -207,7 +245,7 @@ export class GqlApiBuilder extends BaseApiBuilder implements XcMetaMgr { } if (!(await this.xcMeta.isMetaDataExists(this.projectId, this.dbAlias))) { - await this.xcTablesPopulate() + await this.xcTablesPopulate(); } else { await this.xcTablesRead(); } @@ -217,13 +255,19 @@ export class GqlApiBuilder extends BaseApiBuilder implements XcMetaMgr { await this.initGraphqlRoute(); await super.loadCommon(); - this.router.use(`/${this.connectionConfig?.meta?.api?.prefix || 'v1'}`, this.gqlRouter); + this.router.use( + `/${this.connectionConfig?.meta?.api?.prefix || 'v1'}`, + this.gqlRouter + ); const t1 = process.hrtime(t); const t2 = t1[0] + t1[1] / 1000000000; return { - type: "graphql", - apiEndpoint: this.tablesCount ? `/nc/${this.projectId}/${this.connectionConfig?.meta?.api?.prefix || 'v1'}/graphql` : 'Empty database', + type: 'graphql', + apiEndpoint: this.tablesCount + ? `/nc/${this.projectId}/${this.connectionConfig?.meta?.api?.prefix || + 'v1'}/graphql` + : 'Empty database', client: this.connectionConfig.client, databaseName: (this.connectionConfig?.connection as any)?.database, resolversCount: this.resolversCount, @@ -233,15 +277,12 @@ export class GqlApiBuilder extends BaseApiBuilder implements XcMetaMgr { viewsCount: this.viewsCount, functionsCount: this.functionsCount, proceduresCount: this.proceduresCount, - timeTaken: t2.toFixed(1), + timeTaken: t2.toFixed(1) }; - - } public async xcTablesRead(tables?: string[]): Promise { - - this.log(`xcTablesRead : %o`, tables) + this.log(`xcTablesRead : %o`, tables); // todo: load procedure and functions await this.loadXcAcl(); @@ -253,191 +294,287 @@ export class GqlApiBuilder extends BaseApiBuilder implements XcMetaMgr { procedureArr } = await this.readXcModelsAndGroupByType(); + const procedureResolver = new GqlProcedureResolver( + this, + functionArr, + procedureArr, + this.procedureOrFunctionAcls + ); - const procedureResolver = new GqlProcedureResolver(this, functionArr, procedureArr, this.procedureOrFunctionAcls); - - this.log(`xcTablesRead : Generating schema for procedure and resolver`) + this.log(`xcTablesRead : Generating schema for procedure and resolver`); this.schemas.___procedure = procedureResolver.getSchema(); this.resolvers.___procedure = procedureResolver; - const resolversArr = await this.xcMeta.metaList(this.projectId, this.dbAlias, 'nc_resolvers', { - condition: { - handler_type: 1 - } - }); - const middlewaresArr = (await this.xcMeta.metaList(this.projectId, this.dbAlias, 'nc_resolvers', { - condition: { - handler_type: 2 + const resolversArr = await this.xcMeta.metaList( + this.projectId, + this.dbAlias, + 'nc_resolvers', + { + condition: { + handler_type: 1 + } } - })).map(o => { + ); + const middlewaresArr = ( + await this.xcMeta.metaList(this.projectId, this.dbAlias, 'nc_resolvers', { + condition: { + handler_type: 2 + } + }) + ).map(o => { o.functions = JSON.parse(o.functions); return o; }); + const loaderFunctionsArr = await this.xcMeta.metaList( + this.projectId, + this.dbAlias, + 'nc_loaders' + ); - const loaderFunctionsArr = await this.xcMeta.metaList(this.projectId, this.dbAlias, 'nc_loaders'); - - const loaderFunctionsObj = loaderFunctionsArr.reduce((obj, {title, functions}) => { - obj[title] = functions && JSON.parse(functions); - return obj; - }, {}) + const loaderFunctionsObj = loaderFunctionsArr.reduce( + (obj, { title, functions }) => { + obj[title] = functions && JSON.parse(functions); + return obj; + }, + {} + ); this.tablesCount = metaArr.length; for (const meta of tableAndViewArr) { - - if ((tables && !tables.includes(meta.title)) || !enabledModels.includes(meta.title)) { + if ( + (tables && !tables.includes(meta.title)) || + !enabledModels.includes(meta.title) + ) { continue; } - const middlewareBody = middlewaresArr.find(({title}) => title === meta.title)?.functions?.[0]; + const middlewareBody = middlewaresArr.find( + ({ title }) => title === meta.title + )?.functions?.[0]; this.metas[meta.title] = JSON.parse(meta.meta); this.models[meta.title] = this.getBaseModel(this.metas[meta.title]); // tslint:disable-next-line:max-classes-per-file - this.types[meta.title] = class extends XCType { - }; + this.types[meta.title] = class extends XCType {}; this.schemas[meta.title] = meta.schema; - this.policies[meta.title] = resolversArr.filter(({title}) => title === meta.title).reduce((aclObj, { - acl, - resolver - }) => { - aclObj[resolver] = JSON.parse(acl); - return aclObj; - }, {}); - - const functions = resolversArr.filter(({title}) => title === meta.title).reduce((fnObj, { + this.policies[meta.title] = resolversArr + .filter(({ title }) => title === meta.title) + .reduce((aclObj, { acl, resolver }) => { + aclObj[resolver] = JSON.parse(acl); + return aclObj; + }, {}); + + const functions = resolversArr + .filter(({ title }) => title === meta.title) + .reduce((fnObj, { functions, resolver }) => { + fnObj[resolver] = JSON.parse(functions); + return fnObj; + }, {}); + + this.log( + `xcTablesRead : Creating resolvers for '%s' %s`, + meta.title, + meta.type + ); + this.resolvers[meta.title] = new GqlResolver( + this.app as Noco, + this.models, + meta.title, + this.types[meta.title], + this.acls, functions, - resolver - }) => { - fnObj[resolver] = JSON.parse(functions); - return fnObj; - }, {}); - - - this.log(`xcTablesRead : Creating resolvers for '%s' %s`, meta.title, meta.type) - this.resolvers[meta.title] = new GqlResolver(this.app as Noco, this.models, meta.title, this.types[meta.title], this.acls, functions, middlewareBody); + middlewareBody + ); this.resolvers[meta.title].mapResolvers(this.customResolver); } const self = this; - await Promise.all(Object.entries(this.metas).map(async ([tn, schema]) => { - - for (const hm of schema.hasMany) { - - if (!enabledModels.includes(hm.tn)) { - continue; - } + await Promise.all( + Object.entries(this.metas).map(async ([tn, schema]) => { + for (const hm of schema.hasMany) { + if (!enabledModels.includes(hm.tn)) { + continue; + } - if (!hm.enabled) { - continue; - } - const colNameAlias = self.models[hm.rtn]?.columnToAlias[hm.rcn]; + if (!hm.enabled) { + continue; + } + const colNameAlias = self.models[hm.rtn]?.columnToAlias[hm.rcn]; - const middlewareBody = middlewaresArr.find(({title}) => title === hm.tn)?.functions?.[0]; - const countPropName = `${hm._tn}Count`; - const listPropName = `${hm._tn}List`; + const middlewareBody = middlewaresArr.find( + ({ title }) => title === hm.tn + )?.functions?.[0]; + const countPropName = `${hm._tn}Count`; + const listPropName = `${hm._tn}List`; - if (listPropName in this.types[tn].prototype) { - continue; - } + if (listPropName in this.types[tn].prototype) { + continue; + } - const mw = new GqlMiddleware(this.acls, hm.tn, middlewareBody, this.models); - /* has many relation list loader with middleware */ - this.addHmListResolverMethodToType(tn, hm, mw, loaderFunctionsObj, listPropName, colNameAlias); - if (countPropName in this.types[tn].prototype) { - continue; - } - { - const mw = new GqlMiddleware(this.acls, hm.tn, middlewareBody, this.models); + const mw = new GqlMiddleware( + this.acls, + hm.tn, + middlewareBody, + this.models + ); + /* has many relation list loader with middleware */ + this.addHmListResolverMethodToType( + tn, + hm, + mw, + loaderFunctionsObj, + listPropName, + colNameAlias + ); + if (countPropName in this.types[tn].prototype) { + continue; + } + { + const mw = new GqlMiddleware( + this.acls, + hm.tn, + middlewareBody, + this.models + ); - // create count loader with middleware - this.addHmCountResolverMethodToType(hm, mw, tn, loaderFunctionsObj, countPropName, colNameAlias); + // create count loader with middleware + this.addHmCountResolverMethodToType( + hm, + mw, + tn, + loaderFunctionsObj, + countPropName, + colNameAlias + ); + } } - } - for (const mm of schema.manyToMany || []) { + for (const mm of schema.manyToMany || []) { + if (!enabledModels.includes(mm.rtn)) { + continue; + } - if (!enabledModels.includes(mm.rtn)) { - continue; - } + // todo: handle enable/disable + // if (!mm.enabled) { + // continue; + // } - // todo: handle enable/disable - // if (!mm.enabled) { - // continue; - // } + const middlewareBody = middlewaresArr.find( + ({ title }) => title === mm.rtn + )?.functions?.[0]; + // const countPropName = `${mm._rtn}Count`; + const listPropName = `${mm._rtn}MMList`; - const middlewareBody = middlewaresArr.find(({title}) => title === mm.rtn)?.functions?.[0]; - // const countPropName = `${mm._rtn}Count`; - const listPropName = `${mm._rtn}MMList`; + if (listPropName in this.types[tn].prototype) { + continue; + } - if (listPropName in this.types[tn].prototype) { - continue; + const mw = new GqlMiddleware( + this.acls, + mm.tn, + middlewareBody, + this.models + ); + /* has many relation list loader with middleware */ + this.addMMListResolverMethodToType( + tn, + mm, + mw, + {}, + listPropName, + this.metas[mm.tn].columns.find(c => c.pk)._cn + ); + // todo: count + // if (countPropName in this.types[tn].prototype) { + // continue; + // } + // { + // const mw = new GqlMiddleware(this.acls, hm.tn, middlewareBody, this.models); + // + // // create count loader with middleware + // this.addHmCountResolverMethodToType(hm, mw, tn, loaderFunctionsObj, countPropName, colNameAlias); + // } } - const mw = new GqlMiddleware(this.acls, mm.tn, middlewareBody, this.models); - /* has many relation list loader with middleware */ - this.addMMListResolverMethodToType(tn, mm, mw, {}, listPropName, this.metas[mm.tn].columns.find(c => c.pk)._cn); - // todo: count - // if (countPropName in this.types[tn].prototype) { - // continue; - // } - // { - // const mw = new GqlMiddleware(this.acls, hm.tn, middlewareBody, this.models); - // - // // create count loader with middleware - // this.addHmCountResolverMethodToType(hm, mw, tn, loaderFunctionsObj, countPropName, colNameAlias); - // } - } - - for (const bt of schema.belongsTo) { - - - if (!enabledModels.includes(bt.rtn)) { - continue - } + for (const bt of schema.belongsTo) { + if (!enabledModels.includes(bt.rtn)) { + continue; + } - if (!bt.enabled) { - continue; - } - const colNameAlias = self.models[bt.tn]?.columnToAlias[bt.cn]; - const rcolNameAlias = self.models[bt.rtn]?.columnToAlias[bt.rcn]; - const middlewareBody = middlewaresArr.find(({title}) => title === bt.rtn)?.functions?.[0]; - const propName = `${bt._rtn}Read`; - if (propName in this.types[tn].prototype) { - continue; - } + if (!bt.enabled) { + continue; + } + const colNameAlias = self.models[bt.tn]?.columnToAlias[bt.cn]; + const rcolNameAlias = self.models[bt.rtn]?.columnToAlias[bt.rcn]; + const middlewareBody = middlewaresArr.find( + ({ title }) => title === bt.rtn + )?.functions?.[0]; + const propName = `${bt._rtn}Read`; + if (propName in this.types[tn].prototype) { + continue; + } - // create read loader with middleware - { - const mw = new GqlMiddleware(this.acls, bt.rtn, middlewareBody, this.models); - this.log(`xcTablesRead : Creating loader for '%s'`, `${tn}Bt${bt.rtn}`); - this.adBtResolverMethodToType(propName, mw, - tn, bt, rcolNameAlias, colNameAlias, loaderFunctionsObj[`${tn}Bt${bt.rtn}`]); + // create read loader with middleware + { + const mw = new GqlMiddleware( + this.acls, + bt.rtn, + middlewareBody, + this.models + ); + this.log( + `xcTablesRead : Creating loader for '%s'`, + `${tn}Bt${bt.rtn}` + ); + this.adBtResolverMethodToType( + propName, + mw, + tn, + bt, + rcolNameAlias, + colNameAlias, + loaderFunctionsObj[`${tn}Bt${bt.rtn}`] + ); + } } - } - - })); - - + }) + ); } - private addHmListResolverMethodToType(tn: string, hm, mw: GqlMiddleware, loaderFunctionsObj, listPropName: string, colNameAlias) { + private addHmListResolverMethodToType( + tn: string, + hm, + mw: GqlMiddleware, + loaderFunctionsObj, + listPropName: string, + colNameAlias + ) { { const self = this; - this.log(`xcTablesRead : Creating loader for '%s'`, `${tn}Hm${hm.tn}List`) + this.log( + `xcTablesRead : Creating loader for '%s'`, + `${tn}Hm${hm.tn}List` + ); const listLoader = new DataLoader( BaseType.applyMiddlewareForLoader( [mw.middleware], - this.generateLoaderFromStringBody(loaderFunctionsObj[`${tn}Hm${hm.tn}List`]) || (async ids => { - const data = await this.models[tn].hasManyListGQL({ - child: hm.tn, - ids - }) - return ids.map((id: string) => data[id] ? data[id].map(c => new self.types[hm.tn](c)) : []); - }), + this.generateLoaderFromStringBody( + loaderFunctionsObj[`${tn}Hm${hm.tn}List`] + ) || + (async ids => { + const data = await this.models[tn].hasManyListGQL({ + child: hm.tn, + ids + }); + return ids.map((id: string) => + data[id] ? data[id].map(c => new self.types[hm.tn](c)) : [] + ); + }), [mw.postLoaderMiddleware] - )); + ) + ); /* defining HasMany list method within GQL Type class */ Object.defineProperty(this.types[tn].prototype, `${listPropName}`, { @@ -445,29 +582,42 @@ export class GqlApiBuilder extends BaseApiBuilder implements XcMetaMgr { return listLoader.load([this[colNameAlias], args, context, info]); }, configurable: true - }) + }); } } - private addMMListResolverMethodToType(tn: string, mm, mw: GqlMiddleware, _loaderFunctionsObj, listPropName: string, colNameAlias) { + private addMMListResolverMethodToType( + tn: string, + mm, + mw: GqlMiddleware, + _loaderFunctionsObj, + listPropName: string, + colNameAlias + ) { { const self = this; - this.log(`xcTablesRead : Creating loader for '%s'`, `${tn}Mm${mm.rtn}List`) + this.log( + `xcTablesRead : Creating loader for '%s'`, + `${tn}Mm${mm.rtn}List` + ); const listLoader = new DataLoader( BaseType.applyMiddlewareForLoader( [mw.middleware], async parentIds => { - return (await this.models[tn]._getGroupedManyToManyList({ - parentIds, - child: mm.rtn, - // todo: optimize - query only required fields - rest: { - mfields1: '*' - } - }))?.map(child => child.map(c => new self.types[mm.rtn](c))); + return ( + await this.models[tn]._getGroupedManyToManyList({ + parentIds, + child: mm.rtn, + // todo: optimize - query only required fields + rest: { + mfields1: '*' + } + }) + )?.map(child => child.map(c => new self.types[mm.rtn](c))); }, [mw.postLoaderMiddleware] - )); + ) + ); /* defining HasMany list method within GQL Type class */ Object.defineProperty(this.types[tn].prototype, listPropName, { @@ -475,25 +625,39 @@ export class GqlApiBuilder extends BaseApiBuilder implements XcMetaMgr { return listLoader.load([this[colNameAlias], args, context, info]); }, configurable: true - }) + }); } } - private addHmCountResolverMethodToType(hm, mw, tn: string, loaderFunctionsObj, countPropName: string, colNameAlias) { + private addHmCountResolverMethodToType( + hm, + mw, + tn: string, + loaderFunctionsObj, + countPropName: string, + colNameAlias + ) { { - this.log(`xcTablesRead : Creating loader for '%s'`, `${tn}Hm${hm.tn}Count`) + this.log( + `xcTablesRead : Creating loader for '%s'`, + `${tn}Hm${hm.tn}Count` + ); const countLoader = new DataLoader( BaseType.applyMiddlewareForLoader( [mw.middleware], - this.generateLoaderFromStringBody(loaderFunctionsObj[`${tn}Hm${hm.tn}Count`]) || (async (ids: string[]) => { - const data = await this.models[tn].hasManyListCount({ - child: hm.tn, - ids - }) - return data; - }), + this.generateLoaderFromStringBody( + loaderFunctionsObj[`${tn}Hm${hm.tn}Count`] + ) || + (async (ids: string[]) => { + const data = await this.models[tn].hasManyListCount({ + child: hm.tn, + ids + }); + return data; + }), [mw.postLoaderMiddleware] - )); + ) + ); // defining HasMany count method within GQL Type class Object.defineProperty(this.types[tn].prototype, `${countPropName}`, { @@ -501,34 +665,49 @@ export class GqlApiBuilder extends BaseApiBuilder implements XcMetaMgr { return countLoader.load([this[colNameAlias], args, context, info]); }, configurable: true - }) + }); } } - private adBtResolverMethodToType(propName: string, middleware: GqlMiddleware, tableName: string, belongsToRel, rcolNameAlias, colNameAlias, loaderFunc?: any) { + private adBtResolverMethodToType( + propName: string, + middleware: GqlMiddleware, + tableName: string, + belongsToRel, + rcolNameAlias, + colNameAlias, + loaderFunc?: any + ) { const self = this; const readLoader = new DataLoader( BaseType.applyMiddlewareForLoader( [middleware.middleware], - this.generateLoaderFromStringBody(loaderFunc) || (async (ids: string[]) => { - const data = await self.models[belongsToRel.rtn].list({ - limit: ids.length, - where: `(${belongsToRel.rcn},in,${ids.join(',')})` - }) - const gs = _.groupBy(data, rcolNameAlias); - return ids.map(async (id: string) => gs?.[id]?.[0] && new self.types[belongsToRel.rtn](gs[id][0])) - }), + this.generateLoaderFromStringBody(loaderFunc) || + (async (ids: string[]) => { + const data = await self.models[belongsToRel.rtn].list({ + limit: ids.length, + where: `(${belongsToRel.rcn},in,${ids.join(',')})` + }); + const gs = _.groupBy(data, rcolNameAlias); + return ids.map( + async (id: string) => + gs?.[id]?.[0] && new self.types[belongsToRel.rtn](gs[id][0]) + ); + }), [middleware.postLoaderMiddleware] - )); + ) + ); // defining BelongsTo read method within GQL Type class Object.defineProperty(this.types[tableName].prototype, `${propName}`, { async value(args: any, context: any, info: any): Promise { const colName = colNameAlias; - return this[colName] !== null ? readLoader.load([this[colName], args, context, info]) : null; + return this[colName] !== null + ? readLoader.load([this[colName], args, context, info]) + : null; }, configurable: true - }) + }); } public async xcTablesPopulate(args?: { @@ -536,50 +715,59 @@ export class GqlApiBuilder extends BaseApiBuilder implements XcMetaMgr { tn: string; _tn?: string; }>; - type?: 'table' | 'view', + type?: 'table' | 'view'; columns?: { - [key: string]: any - } + [key: string]: any; + }; }): Promise { - this.log('xcTablesPopulate : names - %o , type - %s', args?.tableNames, args?.type) + this.log( + 'xcTablesPopulate : names - %o , type - %s', + args?.tableNames, + args?.type + ); let tables; /* Get all relations */ const relations = await this.relationsSyncAndGet(); // set table name alias relations.forEach(r => { - r._rtn = args?.tableNames?.find(t => t.tn === r.rtn)?._tn || this.getTableNameAlias(r.rtn); - r._tn = args?.tableNames?.find(t => t.tn === r.tn)?._tn || this.getTableNameAlias(r.tn); + r._rtn = + args?.tableNames?.find(t => t.tn === r.rtn)?._tn || + this.getTableNameAlias(r.rtn); + r._tn = + args?.tableNames?.find(t => t.tn === r.tn)?._tn || + this.getTableNameAlias(r.tn); r.enabled = true; - }) - + }); if (args?.tableNames?.length) { - const relatedTableList = [] + const relatedTableList = []; // extract tables which have relation with the tables in list for (const r of relations) { if (args.tableNames.some(t => t.tn === r.tn)) { if (!relatedTableList.includes(r.rtn)) { - relatedTableList.push(r.rtn) - await this.onTableDelete(r.rtn) + relatedTableList.push(r.rtn); + await this.onTableDelete(r.rtn); } } else if (args.tableNames.some(t => t.tn === r.rtn)) { if (!relatedTableList.includes(r.tn)) { - relatedTableList.push(r.tn) - await this.onTableDelete(r.tn) + relatedTableList.push(r.tn); + await this.onTableDelete(r.tn); } } } - tables = args.tableNames.map(({tn, _tn}) => ({ + tables = args.tableNames.map(({ tn, _tn }) => ({ tn, _tn, type: args.type })); - tables.push(...relatedTableList.map(t => ({tn: t}))) + tables.push(...relatedTableList.map(t => ({ tn: t }))); } else { - tables = (await this.sqlClient.tableList())?.data?.list?.filter(({tn}) => !IGNORE_TABLES.includes(tn)); + tables = (await this.sqlClient.tableList())?.data?.list?.filter( + ({ tn }) => !IGNORE_TABLES.includes(tn) + ); // enable extra // tables.push(...(await this.sqlClient.viewList())?.data?.list?.map(v => { @@ -637,52 +825,74 @@ export class GqlApiBuilder extends BaseApiBuilder implements XcMetaMgr { // } } - /* filter based on prefix */ if (this.projectBuilder?.prefix) { tables = tables.filter(t => { - t._tn = t._tn || t.tn.replace(this.projectBuilder?.prefix, '') - return t.tn.startsWith(this.projectBuilder?.prefix) - }) + t._tn = t._tn || t.tn.replace(this.projectBuilder?.prefix, ''); + return t.tn.startsWith(this.projectBuilder?.prefix); + }); } this.tablesCount = tables.length; - if (tables.length) { - - relations.forEach(rel => rel.enabled = true); + relations.forEach(rel => (rel.enabled = true)); const self = this; const tableResolvers = tables.map(table => { return async () => { - /* Filter relations for current table */ - const columns = args?.columns?.[table.tn] || await this.getColumnList(table.tn); - const hasMany = table.type === 'view' ? [] : this.extractHasManyRelationsOfTable(relations, table.tn); - const belongsTo = table.type === 'view' ? [] : this.extractBelongsToRelationsOfTable(relations, table.tn); - const ctx = this.generateContextForTable(table.tn, columns, relations, hasMany, belongsTo, table.type, table?._tn); + const columns = + args?.columns?.[table.tn] || (await this.getColumnList(table.tn)); + const hasMany = + table.type === 'view' + ? [] + : this.extractHasManyRelationsOfTable(relations, table.tn); + const belongsTo = + table.type === 'view' + ? [] + : this.extractBelongsToRelationsOfTable(relations, table.tn); + const ctx = this.generateContextForTable( + table.tn, + columns, + relations, + hasMany, + belongsTo, + table.type, + table?._tn + ); // ctx._tn = table?._tn || ctx._tn; - - this.log(`xcTablesPopulate : Generating model metadata of '%s' %s`, table.tn, table.type); + this.log( + `xcTablesPopulate : Generating model metadata of '%s' %s`, + table.tn, + table.type + ); /**************** prepare table models and policies ****************/ - this.metas[table.tn] = ModelXcMetaFactory.create(this.connectionConfig, this.generateRendererArgs(ctx)).getObject(); + this.metas[table.tn] = ModelXcMetaFactory.create( + this.connectionConfig, + this.generateRendererArgs(ctx) + ).getObject(); this.models[table.tn] = this.getBaseModel(this.metas[table.tn]); await this.generateAndSaveAcl(table.tn, table.type); - const policyGenerator = new ExpressXcTsPolicyGql(this.generateRendererArgs(ctx)); + const policyGenerator = new ExpressXcTsPolicyGql( + this.generateRendererArgs(ctx) + ); this.policies[table.tn] = policyGenerator.getObject(); const functions = {}; - this.log(`xcTablesPopulate : Generating schema of '%s' %s`, table.tn, table.type); + this.log( + `xcTablesPopulate : Generating schema of '%s' %s`, + table.tn, + table.type + ); /**************** prepare GQL: schemas, types, resolvers ****************/ // this.schemas[table.tn] = GqlXcSchemaFactory.create(this.connectionConfig, this.generateRendererArgs(ctx)).getString(); // tslint:disable-next-line:max-classes-per-file - this.types[table.tn] = class extends XCType { - }; + this.types[table.tn] = class extends XCType {}; this.resolvers[table.tn] = new GqlResolver( this.app as Noco, this.models, @@ -693,132 +903,226 @@ export class GqlApiBuilder extends BaseApiBuilder implements XcMetaMgr { '' ); - - if (!(await this.xcMeta.metaGet(this.projectId, this.dbAlias, 'nc_models', {'title': table.tn}))) { - this.log(`xcTablesPopulate : Inserting model metadata of '%s' %s`, table.tn, table.type); - - await this.xcMeta.metaInsert(this.projectId, this.dbAlias, 'nc_models', { - title: table.tn, - type: table.type || 'table', - meta: JSON.stringify(this.metas[table.tn]), - // schema: this.schemas[table.tn], - alias: this.metas[table.tn]._tn, - }) + if ( + !(await this.xcMeta.metaGet( + this.projectId, + this.dbAlias, + 'nc_models', + { title: table.tn } + )) + ) { + this.log( + `xcTablesPopulate : Inserting model metadata of '%s' %s`, + table.tn, + table.type + ); + + await this.xcMeta.metaInsert( + this.projectId, + this.dbAlias, + 'nc_models', + { + title: table.tn, + type: table.type || 'table', + meta: JSON.stringify(this.metas[table.tn]), + // schema: this.schemas[table.tn], + alias: this.metas[table.tn]._tn + } + ); } - - this.log(`xcTablesPopulate : Inserting resolver and middlewaare metadata of '%s' %s`, table.tn, table.type); - if (!(await this.xcMeta.metaGet(this.projectId, this.dbAlias, 'nc_resolvers', {'title': table.tn}))) { - const insertResolvers = Object.entries(this.policies[table.tn]).map(([resolver, acl]) => { - return async () => { - await this.xcMeta.metaInsert(this.projectId, this.dbAlias, 'nc_resolvers', { - title: table.tn, - resolver, - acl: JSON.stringify(acl), - }) + this.log( + `xcTablesPopulate : Inserting resolver and middlewaare metadata of '%s' %s`, + table.tn, + table.type + ); + if ( + !(await this.xcMeta.metaGet( + this.projectId, + this.dbAlias, + 'nc_resolvers', + { title: table.tn } + )) + ) { + const insertResolvers = Object.entries(this.policies[table.tn]).map( + ([resolver, acl]) => { + return async () => { + await this.xcMeta.metaInsert( + this.projectId, + this.dbAlias, + 'nc_resolvers', + { + title: table.tn, + resolver, + acl: JSON.stringify(acl) + } + ); + }; } - }); + ); insertResolvers.push(async () => { - await this.xcMeta.metaInsert(this.projectId, this.dbAlias, 'nc_resolvers', { - title: table.tn, - handler_type: 2 - }) + await this.xcMeta.metaInsert( + this.projectId, + this.dbAlias, + 'nc_resolvers', + { + title: table.tn, + handler_type: 2 + } + ); }); - await NcHelp.executeOperations(insertResolvers, this.connectionConfig.client); + await NcHelp.executeOperations( + insertResolvers, + this.connectionConfig.client + ); } - } + }; }); - await NcHelp.executeOperations(tableResolvers, this.connectionConfig.client); - - await Promise.all(Object.entries(this.metas).map(async ([tn, schema]) => { - - for (const hm of schema.hasMany) { - - - const colNameAlias = self.models[hm.rtn]?.columnToAlias[hm.rcn]; - - const countPropName = `${hm._tn}Count`; - const listPropName = `${hm._tn}List`; - - - this.log(`xcTablesPopulate : Populating '%s' and '%s' loaders`, listPropName, countPropName); - - if (listPropName in this.types[tn].prototype) { - continue; - } - - /* has many relation list loader with middleware */ - const mw = new GqlMiddleware(this.acls, hm.tn, '', this.models); - /* has many relation list loader with middleware */ - this.addHmListResolverMethodToType(tn, hm, mw, {}, listPropName, colNameAlias); - if (countPropName in this.types[tn].prototype) { - continue; - } - { - const mw = new GqlMiddleware(this.acls, hm.tn, null, this.models); - - // create count loader with middleware - this.addHmCountResolverMethodToType(hm, mw, tn, {}, countPropName, colNameAlias); - } - - this.log(`xcTablesPopulate : Inserting loader metadata of '%s' and '%s' loaders`, listPropName, countPropName); - - await this.xcMeta.metaInsert(this.projectId, this.dbAlias, 'nc_loaders', { - title: `${tn}Hm${hm.tn}List`, - parent: tn, - child: hm.tn, - relation: 'hm', - resolver: 'list', - }); - - await this.xcMeta.metaInsert(this.projectId, this.dbAlias, 'nc_loaders', { - title: `${tn}Hm${hm.tn}Count`, - parent: tn, - child: hm.tn, - relation: 'hm', - resolver: 'list', - }); - } - - for (const bt of schema.belongsTo) { - const colNameAlias = self.models[bt.tn]?.columnToAlias[bt.cn]; - const propName = `${bt._rtn}Read`; - + await NcHelp.executeOperations( + tableResolvers, + this.connectionConfig.client + ); - if (propName in this.types[tn].prototype) { - continue; + await Promise.all( + Object.entries(this.metas).map(async ([tn, schema]) => { + for (const hm of schema.hasMany) { + const colNameAlias = self.models[hm.rtn]?.columnToAlias[hm.rcn]; + + const countPropName = `${hm._tn}Count`; + const listPropName = `${hm._tn}List`; + + this.log( + `xcTablesPopulate : Populating '%s' and '%s' loaders`, + listPropName, + countPropName + ); + + if (listPropName in this.types[tn].prototype) { + continue; + } + + /* has many relation list loader with middleware */ + const mw = new GqlMiddleware(this.acls, hm.tn, '', this.models); + /* has many relation list loader with middleware */ + this.addHmListResolverMethodToType( + tn, + hm, + mw, + {}, + listPropName, + colNameAlias + ); + if (countPropName in this.types[tn].prototype) { + continue; + } + { + const mw = new GqlMiddleware(this.acls, hm.tn, null, this.models); + + // create count loader with middleware + this.addHmCountResolverMethodToType( + hm, + mw, + tn, + {}, + countPropName, + colNameAlias + ); + } + + this.log( + `xcTablesPopulate : Inserting loader metadata of '%s' and '%s' loaders`, + listPropName, + countPropName + ); + + await this.xcMeta.metaInsert( + this.projectId, + this.dbAlias, + 'nc_loaders', + { + title: `${tn}Hm${hm.tn}List`, + parent: tn, + child: hm.tn, + relation: 'hm', + resolver: 'list' + } + ); + + await this.xcMeta.metaInsert( + this.projectId, + this.dbAlias, + 'nc_loaders', + { + title: `${tn}Hm${hm.tn}Count`, + parent: tn, + child: hm.tn, + relation: 'hm', + resolver: 'list' + } + ); } - // create read loader with middleware - { - const mw = new GqlMiddleware(this.acls, bt.rtn, null, this.models); - this.log(`xcTablesRead : Creating loader for '%s'`, `${tn}Bt${bt.rtn}`); - this.adBtResolverMethodToType(propName, mw, - tn, bt, colNameAlias, colNameAlias, null); + for (const bt of schema.belongsTo) { + const colNameAlias = self.models[bt.tn]?.columnToAlias[bt.cn]; + const propName = `${bt._rtn}Read`; + + if (propName in this.types[tn].prototype) { + continue; + } + + // create read loader with middleware + { + const mw = new GqlMiddleware( + this.acls, + bt.rtn, + null, + this.models + ); + this.log( + `xcTablesRead : Creating loader for '%s'`, + `${tn}Bt${bt.rtn}` + ); + this.adBtResolverMethodToType( + propName, + mw, + tn, + bt, + colNameAlias, + colNameAlias, + null + ); + } + + this.log( + `xcTablesPopulate : Inserting loader metadata of '%s' loader`, + propName + ); + + await this.xcMeta.metaInsert( + this.projectId, + this.dbAlias, + 'nc_loaders', + { + title: `${tn}Bt${bt.rtn}`, + parent: bt.rtn, + child: tn, + relation: 'bt', + resolver: 'Read' + } + ); } - - - this.log(`xcTablesPopulate : Inserting loader metadata of '%s' loader`, propName); - - await this.xcMeta.metaInsert(this.projectId, this.dbAlias, 'nc_loaders', { - title: `${tn}Bt${bt.rtn}`, - parent: bt.rtn, - child: tn, - relation: 'bt', - resolver: 'Read', - }); - } - - })); + }) + ); await this.getManyToManyRelations(); // generate schema of models for (const meta of Object.values(this.metas)) { /**************** prepare GQL: schemas, types, resolvers ****************/ - this.schemas[meta.tn] = GqlXcSchemaFactory.create(this.connectionConfig, this.generateRendererArgs( - { + this.schemas[meta.tn] = GqlXcSchemaFactory.create( + this.connectionConfig, + this.generateRendererArgs({ ...this.generateContextForTable( meta.tn, meta.columns, @@ -826,75 +1130,94 @@ export class GqlApiBuilder extends BaseApiBuilder implements XcMetaMgr { meta.hasMany, meta.belongsTo, meta.type, - meta._tn, + meta._tn ), manyToMany: meta.manyToMany - })).getString(); + }) + ).getString(); - await this.xcMeta.metaUpdate(this.projectId, this.dbAlias, 'nc_models', { - schema: this.schemas[meta.tn], - }, { - title: meta.tn - }) + await this.xcMeta.metaUpdate( + this.projectId, + this.dbAlias, + 'nc_models', + { + schema: this.schemas[meta.tn] + }, + { + title: meta.tn + } + ); } - // add property in type class for many to many relations - await Promise.all(Object.entries(this.metas).map(async ([tn, meta]) => { - if (!meta.manyToMany) { - return; - } - for (const mm of meta.manyToMany) { - const countPropName = `${mm._rtn}Count`; - const listPropName = `${mm._rtn}MMList`; - - - this.log(`xcTablesPopulate : Populating '%s' and '%s' many to many loaders`, listPropName, countPropName); - - if (listPropName in this.types[tn].prototype) { - continue; + await Promise.all( + Object.entries(this.metas).map(async ([tn, meta]) => { + if (!meta.manyToMany) { + return; } - - /* has many relation list loader with middleware */ - const mw = new GqlMiddleware(this.acls, mm.rtn, '', this.models); - /* has many relation list loader with middleware */ - this.addMMListResolverMethodToType(tn, mm, mw, {}, listPropName, meta.columns.find(c => c.pk)._cn); - // if (countPropName in this.types[tn].prototype) { - // continue; - // } - // { - // const mw = new GqlMiddleware(this.acls, hm.tn, null, this.models); - // - // // create count loader with middleware - // this.addHmCountResolverMethodToType(hm, mw, tn, {}, countPropName, colNameAlias); - // } - // - // this.log(`xcTablesPopulate : Inserting loader metadata of '%s' and '%s' loaders`, listPropName, countPropName); - // - await this.xcMeta.metaInsert(this.projectId, this.dbAlias, 'nc_loaders', { - title: `${tn}Mm${mm.rtn}List`, - parent: tn, - child: mm.rtn, - relation: 'mm', - resolver: 'mmlist', - }); - - // await this.xcMeta.metaInsert(this.projectId, this.dbAlias, 'nc_loaders', { - // title: `${tn}Mm${hm.tn}Count`, - // parent: mm.tn, - // child: mm.rtn, - // relation: 'hm', - // resolver: 'list', - // }); - } - })); - - + for (const mm of meta.manyToMany) { + const countPropName = `${mm._rtn}Count`; + const listPropName = `${mm._rtn}MMList`; + + this.log( + `xcTablesPopulate : Populating '%s' and '%s' many to many loaders`, + listPropName, + countPropName + ); + + if (listPropName in this.types[tn].prototype) { + continue; + } + + /* has many relation list loader with middleware */ + const mw = new GqlMiddleware(this.acls, mm.rtn, '', this.models); + /* has many relation list loader with middleware */ + this.addMMListResolverMethodToType( + tn, + mm, + mw, + {}, + listPropName, + meta.columns.find(c => c.pk)._cn + ); + // if (countPropName in this.types[tn].prototype) { + // continue; + // } + // { + // const mw = new GqlMiddleware(this.acls, hm.tn, null, this.models); + // + // // create count loader with middleware + // this.addHmCountResolverMethodToType(hm, mw, tn, {}, countPropName, colNameAlias); + // } + // + // this.log(`xcTablesPopulate : Inserting loader metadata of '%s' and '%s' loaders`, listPropName, countPropName); + // + await this.xcMeta.metaInsert( + this.projectId, + this.dbAlias, + 'nc_loaders', + { + title: `${tn}Mm${mm.rtn}List`, + parent: tn, + child: mm.rtn, + relation: 'mm', + resolver: 'mmlist' + } + ); + + // await this.xcMeta.metaInsert(this.projectId, this.dbAlias, 'nc_loaders', { + // title: `${tn}Mm${hm.tn}Count`, + // parent: mm.tn, + // child: mm.rtn, + // relation: 'hm', + // resolver: 'list', + // }); + } + }) + ); } - } - public setSchema(key: string, value: string): void { this.log(`setSchema : '%s'`, key); @@ -914,7 +1237,6 @@ export class GqlApiBuilder extends BaseApiBuilder implements XcMetaMgr { await this.xcMeta.metaDelete(this.projectId, this.dbAlias, 'nc_resolvers', { title: tn }); - } public async onHandlerCodeUpdate(tn: string): Promise { @@ -923,135 +1245,250 @@ export class GqlApiBuilder extends BaseApiBuilder implements XcMetaMgr { await this.reInitializeGraphqlEndpoint(); } - // NOTE: xc-meta - public async xcTableRename(oldTablename: string, newTablename: string): Promise { - + public async xcTableRename( + oldTablename: string, + newTablename: string + ): Promise { this.log(`xcTableRename : '%s' => '%s'`, oldTablename, newTablename); //todo: verify the update queries // const metaArr = await (this.sqlClient.knex as XKnex)('nc_models').select(); - const metaArr = await this.xcMeta.metaList(this.projectId, this.dbAlias, 'nc_models'); + const metaArr = await this.xcMeta.metaList( + this.projectId, + this.dbAlias, + 'nc_models' + ); const enabledModels = metaArr.filter(m => m.enabled).map(m => m.title); /* Get all relations */ const relations = await this.getXcRelationList(); - const relatedTableList = this.getRelationTableNames(relations, newTablename, enabledModels); + const relatedTableList = this.getRelationTableNames( + relations, + newTablename, + enabledModels + ); { /* filter relation where this table is present */ - const tableRelations = this.filterRelationsForTable(relations, newTablename); - const hasMany = this.extractHasManyRelationsOfTable(tableRelations, newTablename); - const belongsTo = this.extractBelongsToRelationsOfTable(tableRelations, newTablename); + const tableRelations = this.filterRelationsForTable( + relations, + newTablename + ); + const hasMany = this.extractHasManyRelationsOfTable( + tableRelations, + newTablename + ); + const belongsTo = this.extractBelongsToRelationsOfTable( + tableRelations, + newTablename + ); const columns = await this.getColumnList(newTablename); - const ctx = this.generateContextForTable(newTablename, columns, relations, hasMany, belongsTo); - const enabledModelCtx = this.generateContextForTable(newTablename, columns, - this.filterRelationsForTable(tableRelations, newTablename, enabledModels), - this.extractHasManyRelationsOfTable(hasMany, newTablename, enabledModels), - this.extractBelongsToRelationsOfTable(belongsTo, newTablename, enabledModels) - ) + const ctx = this.generateContextForTable( + newTablename, + columns, + relations, + hasMany, + belongsTo + ); + const enabledModelCtx = this.generateContextForTable( + newTablename, + columns, + this.filterRelationsForTable( + tableRelations, + newTablename, + enabledModels + ), + this.extractHasManyRelationsOfTable( + hasMany, + newTablename, + enabledModels + ), + this.extractBelongsToRelationsOfTable( + belongsTo, + newTablename, + enabledModels + ) + ); // todo: delete resolvers for relation tables - this.log(`xcTableRename : Deleting model with old name '%s'`, oldTablename); + this.log( + `xcTableRename : Deleting model with old name '%s'`, + oldTablename + ); // delete old model delete this.models[oldTablename]; - this.log(`xcTableRename : Generating new model meta for renamed table - '%s' => '%s'`, oldTablename, newTablename); + this.log( + `xcTableRename : Generating new model meta for renamed table - '%s' => '%s'`, + oldTablename, + newTablename + ); /* create models from table */ - const meta = ModelXcMetaFactory.create(this.connectionConfig, this.generateRendererArgs(ctx)).getObject(); + const meta = ModelXcMetaFactory.create( + this.connectionConfig, + this.generateRendererArgs(ctx) + ).getObject(); this.metas[newTablename] = meta; /**************** prepare GQL: schemas, types, resolvers ****************/ if (enabledModels.includes(oldTablename)) { - this.schemas[newTablename] = GqlXcSchemaFactory.create(this.connectionConfig, this.generateRendererArgs(enabledModelCtx)).getString(); + this.schemas[newTablename] = GqlXcSchemaFactory.create( + this.connectionConfig, + this.generateRendererArgs(enabledModelCtx) + ).getString(); } // update old model meta with new details - const existingModel = await this.xcMeta.metaGet(this.projectId, this.dbAlias, 'nc_models', {'title': oldTablename}); + const existingModel = await this.xcMeta.metaGet( + this.projectId, + this.dbAlias, + 'nc_models', + { title: oldTablename } + ); if (existingModel) { // todo: persisting old table_alias and columnAlias const oldMeta = JSON.parse(existingModel.meta); Object.assign(meta, { - columns: oldMeta.columns, + columns: oldMeta.columns }); - this.log(`xcTableRename : Updating model meta - '%s' => '%s'`, oldTablename, newTablename); - - await this.xcMeta.metaUpdate(this.projectId, this.dbAlias, 'nc_models', { - title: newTablename, - meta: JSON.stringify(meta), - schema: this.schemas[newTablename], - alias: meta._tn - }, {'title': oldTablename}) - + this.log( + `xcTableRename : Updating model meta - '%s' => '%s'`, + oldTablename, + newTablename + ); + + await this.xcMeta.metaUpdate( + this.projectId, + this.dbAlias, + 'nc_models', + { + title: newTablename, + meta: JSON.stringify(meta), + schema: this.schemas[newTablename], + alias: meta._tn + }, + { title: oldTablename } + ); } // update resolvers name in db - const newResolvers: any[] = Object.keys(new ExpressXcTsPolicyGql(this.generateRendererArgs(enabledModelCtx)).getObject()); - const oldResolvers: any[] = Object.keys(new ExpressXcTsPolicyGql(this.generateRendererArgs( - this.generateContextForTable(oldTablename, [], [], [], []) - )).getObject()); + const newResolvers: any[] = Object.keys( + new ExpressXcTsPolicyGql( + this.generateRendererArgs(enabledModelCtx) + ).getObject() + ); + const oldResolvers: any[] = Object.keys( + new ExpressXcTsPolicyGql( + this.generateRendererArgs( + this.generateContextForTable(oldTablename, [], [], [], []) + ) + ).getObject() + ); let i = 0; - this.log(`xcTableRename : Updating resolvers name and table name - '%s' => '%s'`, oldTablename, newTablename); + this.log( + `xcTableRename : Updating resolvers name and table name - '%s' => '%s'`, + oldTablename, + newTablename + ); for (const res of newResolvers) { const oldRes = oldResolvers[i++]; - await this.xcMeta.metaUpdate(this.projectId, this.dbAlias, 'nc_resolvers', { - title: newTablename, - resolver: res - }, { - title: oldTablename, - resolver: oldRes, - handler_type: 1 - }) + await this.xcMeta.metaUpdate( + this.projectId, + this.dbAlias, + 'nc_resolvers', + { + title: newTablename, + resolver: res + }, + { + title: oldTablename, + resolver: oldRes, + handler_type: 1 + } + ); } // update resolvers in db - await this.xcMeta.metaUpdate(this.projectId, this.dbAlias, 'nc_resolvers', { - title: newTablename - }, { - title: oldTablename, - handler_type: 2 - }) + await this.xcMeta.metaUpdate( + this.projectId, + this.dbAlias, + 'nc_resolvers', + { + title: newTablename + }, + { + title: oldTablename, + handler_type: 2 + } + ); /* handle relational routes */ for (const hm of meta.hasMany) { - this.log(`xcTableRename : Updating HasMany relation '%s' => `, `${oldTablename}Hm${hm.tn}`, `${newTablename}Hm${hm.tn}`); - - await this.xcMeta.metaUpdate(this.projectId, this.dbAlias, 'nc_loaders', { - title: `${newTablename}Hm${hm.tn}List`, - parent: newTablename - }, { - title: `${oldTablename}Hm${hm.tn}List`, - parent: oldTablename, - child: hm.tn, - relation: 'hm', - resolver: 'list' - }); - await this.xcMeta.metaUpdate(this.projectId, this.dbAlias, 'nc_loaders', { - title: `${newTablename}Hm${hm.tn}Count`, - parent: newTablename - }, { - title: `${oldTablename}Hm${hm.tn}Count`, - parent: oldTablename, - child: hm.tn, - relation: 'hm', - resolver: 'list' - }); + this.log( + `xcTableRename : Updating HasMany relation '%s' => `, + `${oldTablename}Hm${hm.tn}`, + `${newTablename}Hm${hm.tn}` + ); + + await this.xcMeta.metaUpdate( + this.projectId, + this.dbAlias, + 'nc_loaders', + { + title: `${newTablename}Hm${hm.tn}List`, + parent: newTablename + }, + { + title: `${oldTablename}Hm${hm.tn}List`, + parent: oldTablename, + child: hm.tn, + relation: 'hm', + resolver: 'list' + } + ); + await this.xcMeta.metaUpdate( + this.projectId, + this.dbAlias, + 'nc_loaders', + { + title: `${newTablename}Hm${hm.tn}Count`, + parent: newTablename + }, + { + title: `${oldTablename}Hm${hm.tn}Count`, + parent: oldTablename, + child: hm.tn, + relation: 'hm', + resolver: 'list' + } + ); } /* handle belongs to routes and controllers */ for (const bt of meta.belongsTo) { - this.log(`xcTableRename : Updating BelongsTo relation '%s' => '%s'`, `${oldTablename}Bt${bt.rtn}`, `${newTablename}Bt${bt.rtn}`); - await this.xcMeta.metaUpdate(this.projectId, this.dbAlias, 'nc_loaders', { - title: `${newTablename}Bt${bt.rtn}`, - child: newTablename - }, { - title: `${oldTablename}Bt${bt.rtn}`, - parent: bt.rtn, - child: oldTablename, - relation: 'bt', - resolver: 'Read', - }); + this.log( + `xcTableRename : Updating BelongsTo relation '%s' => '%s'`, + `${oldTablename}Bt${bt.rtn}`, + `${newTablename}Bt${bt.rtn}` + ); + await this.xcMeta.metaUpdate( + this.projectId, + this.dbAlias, + 'nc_loaders', + { + title: `${newTablename}Bt${bt.rtn}`, + child: newTablename + }, + { + title: `${oldTablename}Bt${bt.rtn}`, + parent: bt.rtn, + child: oldTablename, + relation: 'bt', + resolver: 'Read' + } + ); } } { @@ -1059,87 +1496,155 @@ export class GqlApiBuilder extends BaseApiBuilder implements XcMetaMgr { // reload routes and update meta for relation tables for (const relationTable of relatedTableList) { const columns = await this.getColumnList(relationTable); - const rHasMany = this.extractHasManyRelationsOfTable(relations, relationTable); - const rBelongsTo = this.extractBelongsToRelationsOfTable(relations, relationTable); - const rCtx = this.generateContextForTable(relationTable, columns, relations, rHasMany, rBelongsTo); - const enabledModelCtx = this.generateContextForTable(relationTable, columns, + const rHasMany = this.extractHasManyRelationsOfTable( + relations, + relationTable + ); + const rBelongsTo = this.extractBelongsToRelationsOfTable( + relations, + relationTable + ); + const rCtx = this.generateContextForTable( + relationTable, + columns, + relations, + rHasMany, + rBelongsTo + ); + const enabledModelCtx = this.generateContextForTable( + relationTable, + columns, this.filterRelationsForTable(relations, newTablename, enabledModels), - this.extractHasManyRelationsOfTable(rHasMany, newTablename, enabledModels), - this.extractBelongsToRelationsOfTable(rBelongsTo, newTablename, enabledModels) - ) + this.extractHasManyRelationsOfTable( + rHasMany, + newTablename, + enabledModels + ), + this.extractBelongsToRelationsOfTable( + rBelongsTo, + newTablename, + enabledModels + ) + ); /* create models from table */ - const rMeta = ModelXcMetaFactory.create(this.connectionConfig, this.generateRendererArgs(rCtx)).getObject(); - this.schemas[relationTable] = GqlXcSchemaFactory.create(this.connectionConfig, this.generateRendererArgs(enabledModelCtx)).getString(); + const rMeta = ModelXcMetaFactory.create( + this.connectionConfig, + this.generateRendererArgs(rCtx) + ).getObject(); + this.schemas[relationTable] = GqlXcSchemaFactory.create( + this.connectionConfig, + this.generateRendererArgs(enabledModelCtx) + ).getString(); // update existing model meta with new details(relation tables) - const rExistingModel = await this.xcMeta.metaGet(this.projectId, this.dbAlias, 'nc_models', {'title': relationTable}); + const rExistingModel = await this.xcMeta.metaGet( + this.projectId, + this.dbAlias, + 'nc_models', + { title: relationTable } + ); if (rExistingModel) { // todo: persisting old table_alias and columnAlias const oldMeta = JSON.parse(rExistingModel.meta); Object.assign(oldMeta, { hasMany: rMeta.hasMany, - belongsTo: rMeta.belongsTo, + belongsTo: rMeta.belongsTo }); - this.log(`xcTableRename : Updating related table model meta - '%s'`, relationTable); + this.log( + `xcTableRename : Updating related table model meta - '%s'`, + relationTable + ); - await this.xcMeta.metaUpdate(this.projectId, this.dbAlias, 'nc_models', { - meta: JSON.stringify(oldMeta), - schema: this.schemas[relationTable] - }, {'title': relationTable}) + await this.xcMeta.metaUpdate( + this.projectId, + this.dbAlias, + 'nc_models', + { + meta: JSON.stringify(oldMeta), + schema: this.schemas[relationTable] + }, + { title: relationTable } + ); this.metas[relationTable] = oldMeta; } - this.models[relationTable] = this.getBaseModel(this.metas[relationTable]); - + this.models[relationTable] = this.getBaseModel( + this.metas[relationTable] + ); // update has many to routes for (const hmRelation of rHasMany) { if (hmRelation.tn === newTablename) { - this.log(`xcTableRename : Updating HasMany relation '%s' => '%s'`, `${relationTable}Hm${oldTablename}`, `${relationTable}Hm${newTablename}`); - - await this.xcMeta.metaUpdate(this.projectId, this.dbAlias, 'nc_loaders', { - title: `${relationTable}Hm${newTablename}List`, - child: newTablename - }, { - title: `${relationTable}Hm${oldTablename}List`, - parent: relationTable, - child: oldTablename, - relation: 'hm', - resolver: 'list' - }); - await this.xcMeta.metaUpdate(this.projectId, this.dbAlias, 'nc_loaders', { - title: `${relationTable}Hm${newTablename}Count`, - child: newTablename - }, { - title: `${relationTable}Hm${oldTablename}Count`, - parent: relationTable, - child: oldTablename, - relation: 'hm', - resolver: 'list' - }); + this.log( + `xcTableRename : Updating HasMany relation '%s' => '%s'`, + `${relationTable}Hm${oldTablename}`, + `${relationTable}Hm${newTablename}` + ); + + await this.xcMeta.metaUpdate( + this.projectId, + this.dbAlias, + 'nc_loaders', + { + title: `${relationTable}Hm${newTablename}List`, + child: newTablename + }, + { + title: `${relationTable}Hm${oldTablename}List`, + parent: relationTable, + child: oldTablename, + relation: 'hm', + resolver: 'list' + } + ); + await this.xcMeta.metaUpdate( + this.projectId, + this.dbAlias, + 'nc_loaders', + { + title: `${relationTable}Hm${newTablename}Count`, + child: newTablename + }, + { + title: `${relationTable}Hm${oldTablename}Count`, + parent: relationTable, + child: oldTablename, + relation: 'hm', + resolver: 'list' + } + ); } } // update belongs to routes for (const btRelation of rBelongsTo) { if (btRelation.rtn === newTablename) { - this.log(`xcTableRename : Updating BelongsTo relation '%s' => '%s'`, `${relationTable}Hm${oldTablename}`, `${relationTable}Hm${newTablename}`); - - await this.xcMeta.metaUpdate(this.projectId, this.dbAlias, 'nc_loaders', { - title: `${relationTable}Bt${newTablename}`, - parent: newTablename - }, { - title: `${relationTable}Bt${oldTablename}`, - parent: oldTablename, - child: relationTable, - relation: 'bt', - resolver: 'Read', - }); + this.log( + `xcTableRename : Updating BelongsTo relation '%s' => '%s'`, + `${relationTable}Hm${oldTablename}`, + `${relationTable}Hm${newTablename}` + ); + + await this.xcMeta.metaUpdate( + this.projectId, + this.dbAlias, + 'nc_loaders', + { + title: `${relationTable}Bt${newTablename}`, + parent: newTablename + }, + { + title: `${relationTable}Bt${oldTablename}`, + parent: oldTablename, + child: relationTable, + relation: 'bt', + resolver: 'Read' + } + ); } } /* Reload relation tables : end */ - } } @@ -1147,12 +1652,12 @@ export class GqlApiBuilder extends BaseApiBuilder implements XcMetaMgr { delete this.schemas[oldTablename]; // load routes and models from db - await this.xcTablesRead([...relatedTableList, newTablename]) + await this.xcTablesRead([...relatedTableList, newTablename]); await this.reInitializeGraphqlEndpoint(); } public async onRelationCreate(tnp: string, tnc: string, args): Promise { - await super.onRelationCreate(tnp, tnc, args) + await super.onRelationCreate(tnp, tnc, args); this.log(`onRelationCreate : Within relation create event handler`); // const self = this; const relations = await this.getXcRelationList(); @@ -1162,27 +1667,48 @@ export class GqlApiBuilder extends BaseApiBuilder implements XcMetaMgr { r._rtn = this.getTableNameAlias(r.rtn); r._tn = this.getTableNameAlias(r.tn); r.enabled = true; - }) + }); /* update parent table meta and resolvers */ { const columns = this.metas[tnp]?.columns; const hasMany = this.extractHasManyRelationsOfTable(relations, tnp); const belongsTo = this.extractBelongsToRelationsOfTable(relations, tnp); - const ctx = this.generateContextForTable(tnp, columns, relations, hasMany, belongsTo); + const ctx = this.generateContextForTable( + tnp, + columns, + relations, + hasMany, + belongsTo + ); ctx.manyToMany = this.metas?.[tnp]?.manyToMany; - const meta = ModelXcMetaFactory.create(this.connectionConfig, {dir: '', ctx, filename: ''}).getObject(); + const meta = ModelXcMetaFactory.create(this.connectionConfig, { + dir: '', + ctx, + filename: '' + }).getObject(); // this.metas[tnp] = meta; - this.schemas[tnp] = GqlXcSchemaFactory.create(this.connectionConfig, this.generateRendererArgs(ctx)).getString(); - + this.schemas[tnp] = GqlXcSchemaFactory.create( + this.connectionConfig, + this.generateRendererArgs(ctx) + ).getString(); // update old model meta with new details - this.log(`onRelationCreate : Generating and updating model meta for parent table '%s'`, tnp); - const existingModel = await this.xcMeta.metaGet(this.projectId, this.dbAlias, 'nc_models', {'title': tnp}); + this.log( + `onRelationCreate : Generating and updating model meta for parent table '%s'`, + tnp + ); + const existingModel = await this.xcMeta.metaGet( + this.projectId, + this.dbAlias, + 'nc_models', + { title: tnp } + ); let queryParams; try { queryParams = JSON.parse(existingModel.query_params); - } catch (e) { /* */ + } catch (e) { + /* */ } if (existingModel) { // todo: persisting old table_alias and columnAlias @@ -1190,41 +1716,64 @@ export class GqlApiBuilder extends BaseApiBuilder implements XcMetaMgr { const oldMeta = JSON.parse(existingModel.meta); meta.hasMany.forEach(hm => { hm.enabled = true; - }) + }); Object.assign(oldMeta, { - hasMany: meta.hasMany, + hasMany: meta.hasMany }); /* Add new has many relation to virtual columns */ oldMeta.v = oldMeta.v || []; oldMeta.v.push({ hm: meta.hasMany.find(hm => hm.rtn === tnp && hm.tn === tnc), - _cn: `${this.getTableNameAlias(tnp)} => ${this.getTableNameAlias(tnc)}` - }) + _cn: `${this.getTableNameAlias(tnp)} => ${this.getTableNameAlias( + tnc + )}` + }); if (queryParams?.showFields) { - queryParams.showFields[`${this.getTableNameAlias(tnp)} => ${this.getTableNameAlias(tnc)}`] = true; + queryParams.showFields[ + `${this.getTableNameAlias(tnp)} => ${this.getTableNameAlias(tnc)}` + ] = true; } this.models[tnp] = this.getBaseModel(oldMeta); - await this.xcMeta.metaUpdate(this.projectId, this.dbAlias, 'nc_models', { - title: tnp, - meta: JSON.stringify(oldMeta), - schema: this.schemas[tnp], - ...(queryParams ? {query_params: JSON.stringify(queryParams)} : {}) - }, {'title': tnp}) + await this.xcMeta.metaUpdate( + this.projectId, + this.dbAlias, + 'nc_models', + { + title: tnp, + meta: JSON.stringify(oldMeta), + schema: this.schemas[tnp], + ...(queryParams + ? { query_params: JSON.stringify(queryParams) } + : {}) + }, + { title: tnp } + ); } const countPropName = `${this.getTableNameAlias(tnc)}Count`; const listPropName = `${this.getTableNameAlias(tnc)}List`; - this.log(`onRelationCreate : Generating and inserting '%s' and '%s' loaders`, countPropName, listPropName); + this.log( + `onRelationCreate : Generating and inserting '%s' and '%s' loaders`, + countPropName, + listPropName + ); - const hm = hasMany.find(rel => rel.tn === tnc) + const hm = hasMany.find(rel => rel.tn === tnc); { /* has many relation list loader with middleware */ const mw = new GqlMiddleware(this.acls, tnc, '', this.models); - this.addHmListResolverMethodToType(tnp, hm, mw, {}, listPropName, this.models[hm.rtn]?.columnToAlias[hm.rcn]); + this.addHmListResolverMethodToType( + tnp, + hm, + mw, + {}, + listPropName, + this.models[hm.rtn]?.columnToAlias[hm.rcn] + ); } /* const listLoader = new DataLoader( BaseType.applyMiddlewareForLoader( @@ -1252,7 +1801,14 @@ export class GqlApiBuilder extends BaseApiBuilder implements XcMetaMgr { // create count loader with middleware { const mw = new GqlMiddleware(this.acls, tnc, '', this.models); - this.addHmListResolverMethodToType(tnp, hm, mw, {}, countPropName, this.models[hm.rtn]?.columnToAlias[hm.rcn]); + this.addHmListResolverMethodToType( + tnp, + hm, + mw, + {}, + countPropName, + this.models[hm.rtn]?.columnToAlias[hm.rcn] + ); /*const countLoader = new DataLoader( BaseType.applyMiddlewareForLoader( @@ -1281,7 +1837,7 @@ export class GqlApiBuilder extends BaseApiBuilder implements XcMetaMgr { parent: tnp, child: tnc, relation: 'hm', - resolver: 'list', + resolver: 'list' }); await this.xcMeta.metaInsert(this.projectId, this.dbAlias, 'nc_loaders', { @@ -1289,7 +1845,7 @@ export class GqlApiBuilder extends BaseApiBuilder implements XcMetaMgr { parent: tnp, child: tnc, relation: 'hm', - resolver: 'list', + resolver: 'list' }); } @@ -1298,52 +1854,86 @@ export class GqlApiBuilder extends BaseApiBuilder implements XcMetaMgr { const columns = this.metas[tnc]?.columns; const belongsTo = this.extractBelongsToRelationsOfTable(relations, tnc); const hasMany = this.extractHasManyRelationsOfTable(relations, tnc); - const ctx = this.generateContextForTable(tnc, columns, relations, hasMany, belongsTo); + const ctx = this.generateContextForTable( + tnc, + columns, + relations, + hasMany, + belongsTo + ); ctx.manyToMany = this.metas?.[tnc]?.manyToMany; - const meta = ModelXcMetaFactory.create(this.connectionConfig, this.generateRendererArgs(ctx)).getObject(); + const meta = ModelXcMetaFactory.create( + this.connectionConfig, + this.generateRendererArgs(ctx) + ).getObject(); // this.metas[tnc] = meta; - this.schemas[tnc] = GqlXcSchemaFactory.create(this.connectionConfig, this.generateRendererArgs(ctx)).getString(); - - - this.log(`onRelationCreate : Generating and updating model meta for child table '%s'`, tnc); + this.schemas[tnc] = GqlXcSchemaFactory.create( + this.connectionConfig, + this.generateRendererArgs(ctx) + ).getString(); + + this.log( + `onRelationCreate : Generating and updating model meta for child table '%s'`, + tnc + ); // update old model meta with new details - const existingModel = await this.xcMeta.metaGet(this.projectId, this.dbAlias, 'nc_models', {'title': tnc}); + const existingModel = await this.xcMeta.metaGet( + this.projectId, + this.dbAlias, + 'nc_models', + { title: tnc } + ); let queryParams; try { queryParams = JSON.parse(existingModel.query_params); - } catch (e) { /* */ + } catch (e) { + /* */ } if (existingModel) { // todo: persisting old table_alias and columnAlias const oldMeta = JSON.parse(existingModel.meta); Object.assign(oldMeta, { - belongsTo: meta.belongsTo, + belongsTo: meta.belongsTo }); /* Add new belongs to relation to virtual columns */ oldMeta.v = oldMeta.v || []; oldMeta.v.push({ bt: meta.belongsTo.find(hm => hm.rtn === tnp && hm.tn === tnc), - _cn: `${this.getTableNameAlias(tnp)} <= ${this.getTableNameAlias(tnc)}` - }) - + _cn: `${this.getTableNameAlias(tnp)} <= ${this.getTableNameAlias( + tnc + )}` + }); if (queryParams?.showFields) { - queryParams.showFields[`${this.getTableNameAlias(tnp)} <= ${this.getTableNameAlias(tnc)}`] = true; + queryParams.showFields[ + `${this.getTableNameAlias(tnp)} <= ${this.getTableNameAlias(tnc)}` + ] = true; } this.models[tnc] = this.getBaseModel(oldMeta); - await this.xcMeta.metaUpdate(this.projectId, this.dbAlias, 'nc_models', { - title: tnc, - meta: JSON.stringify(oldMeta), - schema: this.schemas[tnc], - ...(queryParams ? {query_params: JSON.stringify(queryParams)} : {}) - }, {'title': tnc}) + await this.xcMeta.metaUpdate( + this.projectId, + this.dbAlias, + 'nc_models', + { + title: tnc, + meta: JSON.stringify(oldMeta), + schema: this.schemas[tnc], + ...(queryParams + ? { query_params: JSON.stringify(queryParams) } + : {}) + }, + { title: tnc } + ); } const propName = `${this.getTableNameAlias(tnp)}Read`; - this.log(`onRelationCreate : Generating and inserting'%s' loader`, propName); + this.log( + `onRelationCreate : Generating and inserting'%s' loader`, + propName + ); - const currentRelation = belongsTo.find(rel => rel.rtn === tnp) + const currentRelation = belongsTo.find(rel => rel.rtn === tnp); // create read loader with middleware const mw = new GqlMiddleware(this.acls, tnp, '', this.models); @@ -1382,7 +1972,7 @@ export class GqlApiBuilder extends BaseApiBuilder implements XcMetaMgr { parent: tnp, child: tnc, relation: 'bt', - resolver: 'Read', + resolver: 'Read' }); } @@ -1393,105 +1983,167 @@ export class GqlApiBuilder extends BaseApiBuilder implements XcMetaMgr { this.log(`onPolicyUpdate : Within policy update handler of '%s' table`, tn); await this.xcTablesRead([tn]); - await this.reInitializeGraphqlEndpoint() + await this.reInitializeGraphqlEndpoint(); } public async onRelationDelete(tnp: string, tnc: string, args): Promise { await super.onRelationDelete(tnp, tnc, args); - this.log(`onRelationDelete : Within relation delete handler of '%s' => '%s'`, tnp, tnc); + this.log( + `onRelationDelete : Within relation delete handler of '%s' => '%s'`, + tnp, + tnc + ); const relations = await this.getXcRelationList(); /* update parent table meta and resolvers */ { - const columns = this.metas[tnp]?.columns;//await this.getColumnList(tnp); + const columns = this.metas[tnp]?.columns; //await this.getColumnList(tnp); const hasMany = this.extractHasManyRelationsOfTable(relations, tnp); const belongsTo = this.extractBelongsToRelationsOfTable(relations, tnp); - const ctx = this.generateContextForTable(tnp, columns, relations, hasMany, belongsTo); - const meta = ModelXcMetaFactory.create(this.connectionConfig, {dir: '', ctx, filename: ''}).getObject(); + const ctx = this.generateContextForTable( + tnp, + columns, + relations, + hasMany, + belongsTo + ); + const meta = ModelXcMetaFactory.create(this.connectionConfig, { + dir: '', + ctx, + filename: '' + }).getObject(); - this.schemas[tnp] = GqlXcSchemaFactory.create(this.connectionConfig, this.generateRendererArgs(ctx)).getString(); + this.schemas[tnp] = GqlXcSchemaFactory.create( + this.connectionConfig, + this.generateRendererArgs(ctx) + ).getString(); - this.log(`onRelationDelete : Generating and updating model meta for parent table '%s'`, tnp); + this.log( + `onRelationDelete : Generating and updating model meta for parent table '%s'`, + tnp + ); // update old model meta with new details - const existingModel = await this.xcMeta.metaGet(this.projectId, this.dbAlias, 'nc_models', {'title': tnp}); + const existingModel = await this.xcMeta.metaGet( + this.projectId, + this.dbAlias, + 'nc_models', + { title: tnp } + ); if (existingModel) { // todo: persisting old table_alias and columnAlias const oldMeta = JSON.parse(existingModel.meta); Object.assign(oldMeta, { hasMany: meta.hasMany, - v: oldMeta.v.filter(({hm, lk}) => (!hm || hm.rtn !== tnp || hm.tn !== tnc) && - !(lk && lk.type === 'hm' && lk.rtn === tnp && lk.tn === tnc)) + v: oldMeta.v.filter( + ({ hm, lk }) => + (!hm || hm.rtn !== tnp || hm.tn !== tnc) && + !(lk && lk.type === 'hm' && lk.rtn === tnp && lk.tn === tnc) + ) }); // todo: backup schema - await this.xcMeta.metaUpdate(this.projectId, this.dbAlias, 'nc_models', { - title: tnp, - meta: JSON.stringify(oldMeta), - schema: this.schemas[tnp] - }, {'title': tnp}) + await this.xcMeta.metaUpdate( + this.projectId, + this.dbAlias, + 'nc_models', + { + title: tnp, + meta: JSON.stringify(oldMeta), + schema: this.schemas[tnp] + }, + { title: tnp } + ); this.models[tnp] = this.getBaseModel(oldMeta); - } const countPropName = `${this.getTableNameAlias(tnc)}Count`; const listPropName = `${this.getTableNameAlias(tnc)}List`; - this.log(`onRelationDelete : Deleting '%s' and '%s' loaders`, countPropName, listPropName); + this.log( + `onRelationDelete : Deleting '%s' and '%s' loaders`, + countPropName, + listPropName + ); /* defining HasMany list method within GQL Type class */ - delete this.types[tnp].prototype[`${listPropName}`] + delete this.types[tnp].prototype[`${listPropName}`]; // defining HasMany count method within GQL Type class - delete this.types[tnp].prototype[`${countPropName}`] + delete this.types[tnp].prototype[`${countPropName}`]; await this.xcMeta.metaDelete(this.projectId, this.dbAlias, 'nc_loaders', { parent: tnp, - child: tnc, + child: tnc }); - - } - /* update child table meta and resolvers */ { const columns = await this.getColumnList(tnc); const belongsTo = this.extractBelongsToRelationsOfTable(relations, tnc); const hasMany = this.extractHasManyRelationsOfTable(relations, tnc); - const ctx = this.generateContextForTable(tnc, columns, relations, hasMany, belongsTo); - const meta = ModelXcMetaFactory.create(this.connectionConfig, this.generateRendererArgs(ctx)).getObject(); - - this.schemas[tnc] = GqlXcSchemaFactory.create(this.connectionConfig, this.generateRendererArgs(ctx)).getString(); - - this.log(`onRelationDelete : Generating and updating model meta for child table '%s'`, tnc); + const ctx = this.generateContextForTable( + tnc, + columns, + relations, + hasMany, + belongsTo + ); + const meta = ModelXcMetaFactory.create( + this.connectionConfig, + this.generateRendererArgs(ctx) + ).getObject(); + + this.schemas[tnc] = GqlXcSchemaFactory.create( + this.connectionConfig, + this.generateRendererArgs(ctx) + ).getString(); + + this.log( + `onRelationDelete : Generating and updating model meta for child table '%s'`, + tnc + ); // update old model meta with new details - const existingModel = await this.xcMeta.metaGet(this.projectId, this.dbAlias, 'nc_models', {'title': tnc}); + const existingModel = await this.xcMeta.metaGet( + this.projectId, + this.dbAlias, + 'nc_models', + { title: tnc } + ); if (existingModel) { // todo: persisting old table_alias and columnAlias const oldMeta = JSON.parse(existingModel.meta); Object.assign(oldMeta, { belongsTo: meta.belongsTo, - v: oldMeta.v.filter(({bt, lk}) => (!bt || bt.rtn !== tnp || bt.tn !== tnc) && - !(lk && lk.type === 'bt' && lk.rtn === tnp && lk.tn === tnc)) + v: oldMeta.v.filter( + ({ bt, lk }) => + (!bt || bt.rtn !== tnp || bt.tn !== tnc) && + !(lk && lk.type === 'bt' && lk.rtn === tnp && lk.tn === tnc) + ) }); - await this.xcMeta.metaUpdate(this.projectId, this.dbAlias, 'nc_models', { - title: tnc, - meta: JSON.stringify(oldMeta), - schema: this.schemas[tnc] - }, {'title': tnc}); + await this.xcMeta.metaUpdate( + this.projectId, + this.dbAlias, + 'nc_models', + { + title: tnc, + meta: JSON.stringify(oldMeta), + schema: this.schemas[tnc] + }, + { title: tnc } + ); this.models[tnc] = this.getBaseModel(oldMeta); } const propName = `${this.getTableNameAlias(tnp)}Read`; this.log(`onRelationDelete : Deleting '%s' loader`, propName); - // defining BelongsTo read method within GQL Type class - delete this.types[tnc].prototype[`${propName}`] + delete this.types[tnc].prototype[`${propName}`]; await this.xcMeta.metaDelete(this.projectId, this.dbAlias, 'nc_loaders', { parent: tnp, - child: tnc, + child: tnc }); } @@ -1499,32 +2151,44 @@ export class GqlApiBuilder extends BaseApiBuilder implements XcMetaMgr { this.models[tnp] = this.getBaseModel(this.metas[tnp]); await this.reInitializeGraphqlEndpoint(); - } public async onTableUpdate(changeObj: any): Promise { this.log(`onTableUpdate : '%s'`, changeObj.tn); - await super.onTableUpdate(changeObj, async ({ctx}) => { - - + await super.onTableUpdate(changeObj, async ({ ctx }) => { const tn = changeObj.tn; - const metaArr = await this.xcMeta.metaList(this.projectId, this.dbAlias, 'nc_models'); + const metaArr = await this.xcMeta.metaList( + this.projectId, + this.dbAlias, + 'nc_models' + ); const enabledModels = metaArr.filter(m => m.enabled).map(m => m.title); if (!enabledModels.includes(changeObj.tn)) { - return + return; } - const enabledModelCtx = this.generateContextForTable(tn, ctx.columns, + const enabledModelCtx = this.generateContextForTable( + tn, + ctx.columns, this.filterRelationsForTable(ctx.relations, tn, enabledModels), this.extractHasManyRelationsOfTable(ctx.hasMany, tn, enabledModels), this.extractBelongsToRelationsOfTable(ctx.belongsTo, tn, enabledModels) - ) + ); const oldSchema = this.schemas[tn]; - this.log(`onTableUpdate : Populating new schema for '%s' table`, changeObj.tn); - this.schemas[tn] = GqlXcSchemaFactory.create(this.connectionConfig, this.generateRendererArgs(enabledModelCtx)).getString(); + this.log( + `onTableUpdate : Populating new schema for '%s' table`, + changeObj.tn + ); + this.schemas[tn] = GqlXcSchemaFactory.create( + this.connectionConfig, + this.generateRendererArgs(enabledModelCtx) + ).getString(); if (oldSchema !== this.schemas[tn]) { - this.log(`onTableUpdate : Updating and taking backup of schema - '%s' table`, changeObj.tn); + this.log( + `onTableUpdate : Updating and taking backup of schema - '%s' table`, + changeObj.tn + ); // const oldModel = await this.xcMeta.metaGet(this.projectId, this.dbAlias, 'nc_models', { // title: tn @@ -1536,13 +2200,18 @@ export class GqlApiBuilder extends BaseApiBuilder implements XcMetaMgr { // previousSchemas = [...JSON.parse(oldModel.schema_previous), oldSchema].slice(-5); // } - await this.xcMeta.metaUpdate(this.projectId, this.dbAlias, 'nc_models', { - schema: this.schemas[tn], - // schema_previous: JSON.stringify(previousSchemas) - }, { - title: tn - }); - + await this.xcMeta.metaUpdate( + this.projectId, + this.dbAlias, + 'nc_models', + { + schema: this.schemas[tn] + // schema_previous: JSON.stringify(previousSchemas) + }, + { + title: tn + } + ); } }); await this.reInitializeGraphqlEndpoint(); @@ -1551,24 +2220,38 @@ export class GqlApiBuilder extends BaseApiBuilder implements XcMetaMgr { public async onViewUpdate(viewName: string): Promise { this.log(`onViewUpdate : '%s'`, viewName); - await super.onViewUpdate(viewName, async ({ctx, meta}) => { - const metaArr = await this.xcMeta.metaList(this.projectId, this.dbAlias, 'nc_models'); + await super.onViewUpdate(viewName, async ({ ctx, meta }) => { + const metaArr = await this.xcMeta.metaList( + this.projectId, + this.dbAlias, + 'nc_models' + ); const enabledModels = metaArr.filter(m => m.enabled).map(m => m.title); if (!enabledModels.includes(viewName)) { - return + return; } - const enabledModelCtx = this.generateContextForTable(viewName, ctx.columns, - [], [], [], 'view' - ) + const enabledModelCtx = this.generateContextForTable( + viewName, + ctx.columns, + [], + [], + [], + 'view' + ); const oldSchema = this.schemas[viewName]; this.log(`onViewUpdate : Populating new schema for '%s' view`, viewName); - meta.schema = this.schemas[viewName] = GqlXcSchemaFactory.create(this.connectionConfig, this.generateRendererArgs(enabledModelCtx)).getString(); + meta.schema = this.schemas[viewName] = GqlXcSchemaFactory.create( + this.connectionConfig, + this.generateRendererArgs(enabledModelCtx) + ).getString(); if (oldSchema !== this.schemas[viewName]) { - - this.log(`onViewUpdate : Updating and taking backup of schema - '%s' view`, viewName); + this.log( + `onViewUpdate : Updating and taking backup of schema - '%s' view`, + viewName + ); // const oldModel = await this.xcMeta.metaGet(this.projectId, this.dbAlias, 'nc_models', { // title: viewName @@ -1580,18 +2263,23 @@ export class GqlApiBuilder extends BaseApiBuilder implements XcMetaMgr { // previousSchemas = [...JSON.parse(oldModel.schema_previous), oldSchema].slice(-5); // } - await this.xcMeta.metaUpdate(this.projectId, this.dbAlias, 'nc_models', { - schema: meta.schema, - // schema_previous: JSON.stringify(previousSchemas) - }, { - title: viewName - }); + await this.xcMeta.metaUpdate( + this.projectId, + this.dbAlias, + 'nc_models', + { + schema: meta.schema + // schema_previous: JSON.stringify(previousSchemas) + }, + { + title: viewName + } + ); } }); await this.reInitializeGraphqlEndpoint(); } - public async onGqlSchemaUpdate(tn: string, schema: string): Promise { this.log(`onGqlSchemaUpdate : '%s'`, tn); this.schemas[tn] = schema; @@ -1601,47 +2289,72 @@ export class GqlApiBuilder extends BaseApiBuilder implements XcMetaMgr { public async onMiddlewareCodeUpdate(tn: string): Promise { this.log(`onMiddlewareCodeUpdate : '%s'`, tn); - this.log(`onMiddlewareCodeUpdate : Updating middleware code in meta table - '%s' table`, tn); + this.log( + `onMiddlewareCodeUpdate : Updating middleware code in meta table - '%s' table`, + tn + ); - const middleware = await this.xcMeta.metaGet(this.projectId, this.dbAlias, 'nc_resolvers', { - handler_type: 2, - title: tn - }); + const middleware = await this.xcMeta.metaGet( + this.projectId, + this.dbAlias, + 'nc_resolvers', + { + handler_type: 2, + title: tn + } + ); let middlewareBody = null; if (middleware.functions) { try { middlewareBody = JSON.parse(middleware.functions)[0]; } catch (e) { - console.log(e.message) + console.log(e.message); } } - this.log(`onMiddlewareCodeUpdate : Updating resolvers with new middleware - '%s' table`, tn); + this.log( + `onMiddlewareCodeUpdate : Updating resolvers with new middleware - '%s' table`, + tn + ); this.resolvers[tn].updateMiddlewareBody(middlewareBody); await this.reInitializeGraphqlEndpoint(); // todo: update middleware for loaders - } public async onToggleModels(enabledModels: string[]): Promise { this.log(`onToggleModels : %o`, enabledModels); - /* Get all relations */ const relations = await this.getXcRelationList(); const generateNewSchemas = enabledModels.map(tn => { return async () => { /* Filter relations for current table */ const columns = await this.getColumnList(tn); - const hasMany = this.extractHasManyRelationsOfTable(relations, tn, enabledModels); - const belongsTo = this.extractBelongsToRelationsOfTable(relations, tn, enabledModels); - const ctx = this.generateContextForTable(tn, columns, [...hasMany, ...belongsTo], hasMany, belongsTo); - + const hasMany = this.extractHasManyRelationsOfTable( + relations, + tn, + enabledModels + ); + const belongsTo = this.extractBelongsToRelationsOfTable( + relations, + tn, + enabledModels + ); + const ctx = this.generateContextForTable( + tn, + columns, + [...hasMany, ...belongsTo], + hasMany, + belongsTo + ); this.log(`onToggleModels : Generating new schema for '%s'`, tn); - const newSchemaa = GqlXcSchemaFactory.create(this.connectionConfig, this.generateRendererArgs(ctx)).getString(); + const newSchemaa = GqlXcSchemaFactory.create( + this.connectionConfig, + this.generateRendererArgs(ctx) + ).getString(); if (newSchemaa !== this.schemas[tn]) { // const oldModel = await this.xcMeta.metaGet(this.projectId, this.dbAlias, 'nc_models', { @@ -1654,17 +2367,26 @@ export class GqlApiBuilder extends BaseApiBuilder implements XcMetaMgr { // previousSchemas = [...JSON.parse(oldModel.schema_previous), [this.schemas[tn]]].slice(-5); // } this.schemas[tn] = newSchemaa; - await this.xcMeta.metaUpdate(this.projectId, this.dbAlias, 'nc_models', { - schema: this.schemas[tn], - // schema_previous: JSON.stringify(previousSchemas) - }, { - title: tn - }) + await this.xcMeta.metaUpdate( + this.projectId, + this.dbAlias, + 'nc_models', + { + schema: this.schemas[tn] + // schema_previous: JSON.stringify(previousSchemas) + }, + { + title: tn + } + ); } - } + }; }); - await NcHelp.executeOperations(generateNewSchemas, this.connectionConfig.client); + await NcHelp.executeOperations( + generateNewSchemas, + this.connectionConfig.client + ); this.metas = {}; this.schemas = {}; this.resolvers = {}; @@ -1675,7 +2397,10 @@ export class GqlApiBuilder extends BaseApiBuilder implements XcMetaMgr { public async onViewCreate(viewName: string): Promise { this.log(`onViewCreate : '%s'`, viewName); - await this.xcTablesPopulate({tableNames: [{tn: viewName}], type: 'view'}); + await this.xcTablesPopulate({ + tableNames: [{ tn: viewName }], + type: 'view' + }); await this.reInitializeGraphqlEndpoint(); } @@ -1684,22 +2409,25 @@ export class GqlApiBuilder extends BaseApiBuilder implements XcMetaMgr { const functions = (await this.sqlClient.functionList())?.data?.list; // do insertion parallelly - const functionObj = functions.find(f => f.function_name === functionName) + const functionObj = functions.find(f => f.function_name === functionName); if (functionObj) { - this.log(`onFunctionCreate : Generating and inserting '%s' function meta and acl`, functionName); + this.log( + `onFunctionCreate : Generating and inserting '%s' function meta and acl`, + functionName + ); await this.generateAndSaveAcl(functionObj.function_name, 'function'); await this.xcMeta.metaInsert(this.projectId, this.dbAlias, 'nc_models', { title: functionObj.function_name, - meta: JSON.stringify({...functionObj, type: 'function'}), + meta: JSON.stringify({ ...functionObj, type: 'function' }), type: 'function' - }) + }); } this.generateAndSaveAcl(functionName, 'function'); this.resolvers.___procedure.functionsSet(functions); this.schemas.___procedure = this.resolvers.___procedure.getSchema(); - await this.reInitializeGraphqlEndpoint() + await this.reInitializeGraphqlEndpoint(); } public async onFunctionDelete(functionName: string): Promise { @@ -1723,16 +2451,21 @@ export class GqlApiBuilder extends BaseApiBuilder implements XcMetaMgr { const procedures = (await this.sqlClient.procedureList())?.data?.list; // do insertion parallelly - const procedureObj = procedures.find(f => f.procedure_name === procedureName) + const procedureObj = procedures.find( + f => f.procedure_name === procedureName + ); if (procedureObj) { - this.log(`onProcedureCreate :Generating and inserting '%s' procedure meta and acl`, procedureName); + this.log( + `onProcedureCreate :Generating and inserting '%s' procedure meta and acl`, + procedureName + ); await this.generateAndSaveAcl(procedureObj.procedure_name, 'procedure'); await this.xcMeta.metaInsert(this.projectId, this.dbAlias, 'nc_models', { title: procedureObj.procedure_name, - meta: JSON.stringify({...procedureObj, type: 'procedure'}), + meta: JSON.stringify({ ...procedureObj, type: 'procedure' }), type: 'procedure' - }) + }); } this.generateAndSaveAcl(procedureName, 'procedure'); @@ -1757,71 +2490,81 @@ export class GqlApiBuilder extends BaseApiBuilder implements XcMetaMgr { } private async reInitializeGraphqlEndpoint(): Promise { - - this.log(`reInitializeGraphqlEndpoint : Reinitializing graphql router endpoint`); + this.log( + `reInitializeGraphqlEndpoint : Reinitializing graphql router endpoint` + ); await this.initGraphqlRoute(); const grIndex = this.gqlRouter.stack.findIndex(r => { return r?.regexp?.test('/graphql/'); - }) - this.gqlRouter.stack.splice(grIndex, 1) - + }); + this.gqlRouter.stack.splice(grIndex, 1); } - private async initGraphqlRoute(): Promise { - this.log(`initGraphqlRoute : Initializing graphql router endpoint`); try { - - const {mergeResolvers, mergeTypeDefs} = await import('@graphql-tools/merge'); - const {graphqlHTTP} = await import('express-graphql'); - const {buildSchema} = await import('graphql'); - const {default: depthLimit} = await import('graphql-depth-limit'); - + const { mergeResolvers, mergeTypeDefs } = await import( + '@graphql-tools/merge' + ); + const { graphqlHTTP } = await import('express-graphql'); + const { buildSchema } = await import('graphql'); + const { default: depthLimit } = await import('graphql-depth-limit'); this.log(`initGraphqlRoute : Merging resolvers`); - const rootValue = mergeResolvers([{ - nocodb_health() { - return 'Coming soon' + const rootValue = mergeResolvers([ + { + nocodb_health() { + return 'Coming soon'; + }, + m2mNotChildren: m2mNotChildren({ models: this.models }), + m2mNotChildrenCount: m2mNotChildrenCount({ models: this.models }), + JSON: GraphQLJSON }, - m2mNotChildren: m2mNotChildren({models: this.models}), - m2mNotChildrenCount: m2mNotChildrenCount({models: this.models}), - JSON: GraphQLJSON, - }, ...Object.values(this.resolvers).map(r => r.mapResolvers(this.customResolver))]); + ...Object.values(this.resolvers).map(r => + r.mapResolvers(this.customResolver) + ) + ]); this.log(`initGraphqlRoute : Building graphql schema`); - const schemaStr = mergeTypeDefs([ - ...Object.values(this.schemas).filter(Boolean), - ` ${this.customResolver?.schema || ''} \n ${commonSchema}`, - // ...this.typesWithFormulaProps - ], { - commentDescriptions: true, - forceSchemaDefinition: true, - reverseDirectives: true, - throwOnConflict: true, - useSchemaDefinition: true - }); + const schemaStr = mergeTypeDefs( + [ + ...Object.values(this.schemas).filter(Boolean), + ` ${this.customResolver?.schema || ''} \n ${commonSchema}` + // ...this.typesWithFormulaProps + ], + { + commentDescriptions: true, + forceSchemaDefinition: true, + reverseDirectives: true, + throwOnConflict: true, + useSchemaDefinition: true + } + ); const schema = buildSchema(schemaStr); - - this.log(`initGraphqlRoute : Initializing graphql endpoint - '%s'`, '/graphql'); + this.log( + `initGraphqlRoute : Initializing graphql endpoint - '%s'`, + '/graphql' + ); this.gqlRouter.use('/graphql', (req, res, next) => { graphqlHTTP({ - context: ({req, res, next}), + context: { req, res, next }, graphiql: { headerEditorEnabled: true }, rootValue, schema, validationRules: [ - depthLimit(this.connectionConfig?.meta?.api?.graphqlDepthLimit ?? 10), + depthLimit( + this.connectionConfig?.meta?.api?.graphqlDepthLimit ?? 10 + ) ], - customExecuteFn: async (args) => { + customExecuteFn: async args => { const data = await execute(args); return data; - }, + } })(req, res); }); @@ -1831,7 +2574,6 @@ export class GqlApiBuilder extends BaseApiBuilder implements XcMetaMgr { } } - private generateLoaderFromStringBody(fnBody: string[]): any { this.log(`generateLoaderFromStringBody : `); // @ts-ignore @@ -1840,7 +2582,7 @@ export class GqlApiBuilder extends BaseApiBuilder implements XcMetaMgr { return; } // @ts-ignore - const handler = (ids) => { + const handler = ids => { return []; }; @@ -1851,93 +2593,143 @@ export class GqlApiBuilder extends BaseApiBuilder implements XcMetaMgr { // tslint:disable-next-line:no-eval eval(js); } catch (e) { - console.log('Error in transpilation', e) + console.log('Error in transpilation', e); } - return handler; } - private log(str, ...args): void { log(`${this.dbAlias} : ${str}`, ...args); } - public async onManyToManyRelationCreate(parent: string, child: string, args?: any): Promise> { + public async onManyToManyRelationCreate( + parent: string, + child: string, + args?: any + ): Promise> { const res = await super.onManyToManyRelationCreate(parent, child, args); for (const tn of [parent, child]) { const meta = this.metas[tn]; - const {columns, hasMany, belongsTo, manyToMany} = meta; - const ctx = this.generateContextForTable(tn, columns, [...hasMany, ...belongsTo], hasMany, belongsTo); + const { columns, hasMany, belongsTo, manyToMany } = meta; + const ctx = this.generateContextForTable( + tn, + columns, + [...hasMany, ...belongsTo], + hasMany, + belongsTo + ); ctx.manyToMany = manyToMany; - this.schemas[tn] = GqlXcSchemaFactory.create(this.connectionConfig, this.generateRendererArgs(ctx)).getString(); + this.schemas[tn] = GqlXcSchemaFactory.create( + this.connectionConfig, + this.generateRendererArgs(ctx) + ).getString(); // todo: update schema history - await this.xcMeta.metaUpdate(this.projectId, this.dbAlias, 'nc_models', { - schema: this.schemas[tn] - }, { - title: tn - }) + await this.xcMeta.metaUpdate( + this.projectId, + this.dbAlias, + 'nc_models', + { + schema: this.schemas[tn] + }, + { + title: tn + } + ); } { const listPropName = `${this.metas[child]._tn}MMList`; - this.log(`onRelationCreate : Generating and inserting '%s' loaders`, listPropName); + this.log( + `onRelationCreate : Generating and inserting '%s' loaders`, + listPropName + ); /* has many relation list loader with middleware */ const mw = new GqlMiddleware(this.acls, parent, '', this.models); - this.addMMListResolverMethodToType(parent, {rtn: child}, mw, {}, listPropName, this.metas[parent].columns.find(c => c.pk)._cn) + this.addMMListResolverMethodToType( + parent, + { rtn: child }, + mw, + {}, + listPropName, + this.metas[parent].columns.find(c => c.pk)._cn + ); } { const listPropName = `${this.metas[parent]._tn}MMList`; - this.log(`onRelationCreate : Generating and inserting '%s' loaders`, listPropName); + this.log( + `onRelationCreate : Generating and inserting '%s' loaders`, + listPropName + ); /* has many relation list loader with middleware */ const mw = new GqlMiddleware(this.acls, child, '', this.models); - this.addMMListResolverMethodToType(child, {rtn: parent}, mw, {}, listPropName, this.metas[child].columns.find(c => c.pk)._cn) + this.addMMListResolverMethodToType( + child, + { rtn: parent }, + mw, + {}, + listPropName, + this.metas[child].columns.find(c => c.pk)._cn + ); } - await this.xcMeta.metaInsert(this.projectId, this.dbAlias, 'nc_loaders', { title: `${parent}Mm${child}List`, parent, child, relation: 'mm', - resolver: 'list', + resolver: 'list' }); await this.xcMeta.metaInsert(this.projectId, this.dbAlias, 'nc_loaders', { title: `${child}Mm${parent}List`, parent: child, child: parent, relation: 'mm', - resolver: 'list', + resolver: 'list' }); await this.reInitializeGraphqlEndpoint(); return res; } - public async onManyToManyRelationDelete(parent: string, child: string, args?: any) { - - await super.onManyToManyRelationDelete(parent, child, args) + public async onManyToManyRelationDelete( + parent: string, + child: string, + args?: any + ) { + await super.onManyToManyRelationDelete(parent, child, args); for (const tn of [parent, child]) { const meta = this.metas[tn]; - const {columns, hasMany, belongsTo, manyToMany} = meta; - const ctx = this.generateContextForTable(tn, columns, [...hasMany, ...belongsTo], hasMany, belongsTo); + const { columns, hasMany, belongsTo, manyToMany } = meta; + const ctx = this.generateContextForTable( + tn, + columns, + [...hasMany, ...belongsTo], + hasMany, + belongsTo + ); this.schemas[tn] = GqlXcSchemaFactory.create(this.connectionConfig, { ...this.generateRendererArgs(ctx), manyToMany }).getString(); // todo: update schema history - await this.xcMeta.metaUpdate(this.projectId, this.dbAlias, 'nc_models', { - schema: this.schemas[tn] - }, { - title: tn - }) + await this.xcMeta.metaUpdate( + this.projectId, + this.dbAlias, + 'nc_models', + { + schema: this.schemas[tn] + }, + { + title: tn + } + ); } await this.reInitializeGraphqlEndpoint(); } - protected async ncUpManyToMany(): Promise { const metas = await super.ncUpManyToMany(); @@ -1945,7 +2737,15 @@ export class GqlApiBuilder extends BaseApiBuilder implements XcMetaMgr { return; } for (const meta of metas) { - const ctx = this.generateContextForTable(meta.tn, meta.columns, [], meta.hasMany, meta.belongsTo, meta.type, meta._tn); + const ctx = this.generateContextForTable( + meta.tn, + meta.columns, + [], + meta.hasMany, + meta.belongsTo, + meta.type, + meta._tn + ); /* generate gql schema of the table */ const schema = GqlXcSchemaFactory.create(this.connectionConfig, { @@ -1958,29 +2758,38 @@ export class GqlApiBuilder extends BaseApiBuilder implements XcMetaMgr { }).getString(); /* update schema in metadb */ - await this.xcMeta.metaUpdate(this.projectId, this.dbAlias, 'nc_models', { - schema - }, { - title: meta.tn, - type: 'table' - }) + await this.xcMeta.metaUpdate( + this.projectId, + this.dbAlias, + 'nc_models', + { + schema + }, + { + title: meta.tn, + type: 'table' + } + ); // todo : add loaders if (meta.manyToMany) { for (const mm of meta.manyToMany) { - await this.xcMeta.metaInsert(this.projectId, this.dbAlias, 'nc_loaders', { - title: `${mm.tn}Mm${mm.rtn}List`, - parent: mm.tn, - child: mm.rtn, - relation: 'mm', - resolver: 'mmlist', - }); + await this.xcMeta.metaInsert( + this.projectId, + this.dbAlias, + 'nc_loaders', + { + title: `${mm.tn}Mm${mm.rtn}List`, + parent: mm.tn, + child: mm.rtn, + relation: 'mm', + resolver: 'mmlist' + } + ); } } - } - } /* // todo: dump it in db @@ -2001,24 +2810,28 @@ export class GqlApiBuilder extends BaseApiBuilder implements XcMetaMgr { return schemas; }*/ - async onMetaUpdate(tn: string): Promise { await super.onMetaUpdate(tn); const meta = this.metas[tn]; - const ctx = this.generateContextForTable(tn, meta.columns, + const ctx = this.generateContextForTable( + tn, + meta.columns, [...meta.belongsTo, meta.hasMany], meta.hasMany, meta.belongsTo - ) + ); const oldSchema = this.schemas[tn]; // this.log(`onTableUpdate : Populating new schema for '%s' table`, changeObj.tn); // meta.schema = - this.schemas[tn] = GqlXcSchemaFactory.create(this.connectionConfig, this.generateRendererArgs({ - ...meta, - ...ctx - })).getString(); + this.schemas[tn] = GqlXcSchemaFactory.create( + this.connectionConfig, + this.generateRendererArgs({ + ...meta, + ...ctx + }) + ).getString(); if (oldSchema !== this.schemas[tn]) { // this.log(`onTableUpdate : Updating and taking backup of schema - '%s' table`, tn); @@ -2032,17 +2845,21 @@ export class GqlApiBuilder extends BaseApiBuilder implements XcMetaMgr { // previousSchemas = [...JSON.parse(oldModel.schema_previous), oldSchema].slice(-5); // } - await this.xcMeta.metaUpdate(this.projectId, this.dbAlias, 'nc_models', { - schema: this.schemas[tn], - // schema_previous: JSON.stringify(previousSchemas) - }, { - title: tn, - type: 'table' - }); - + await this.xcMeta.metaUpdate( + this.projectId, + this.dbAlias, + 'nc_models', + { + schema: this.schemas[tn] + // schema_previous: JSON.stringify(previousSchemas) + }, + { + title: tn, + type: 'table' + } + ); } - return this.reInitializeGraphqlEndpoint(); } } @@ -2069,5 +2886,3 @@ export class GqlApiBuilder extends BaseApiBuilder implements XcMetaMgr { * along with this program. If not, see . * */ - - diff --git a/packages/nocodb/src/lib/noco/gql/GqlAuthResolver.ts b/packages/nocodb/src/lib/noco/gql/GqlAuthResolver.ts index ea293821f5..ff538318e8 100644 --- a/packages/nocodb/src/lib/noco/gql/GqlAuthResolver.ts +++ b/packages/nocodb/src/lib/noco/gql/GqlAuthResolver.ts @@ -1,45 +1,46 @@ -import {promisify} from 'util'; +import { promisify } from 'util'; import bcrypt from 'bcryptjs'; import * as ejs from 'ejs'; import * as jwt from 'jsonwebtoken'; import passport from 'passport'; -import {ExtractJwt, Strategy} from 'passport-jwt'; +import { ExtractJwt, Strategy } from 'passport-jwt'; -import IEmailAdapter from "../../../interface/IEmailAdapter"; -import {DbConfig, NcConfig} from "../../../interface/config"; -import {Knex, XKnex} from "../../dataMapper"; -import Noco from "../Noco"; +import IEmailAdapter from '../../../interface/IEmailAdapter'; +import { DbConfig, NcConfig } from '../../../interface/config'; +import { Knex, XKnex } from '../../dataMapper'; +import Noco from '../Noco'; import authSchema from './auth/schema'; -const {v4: uuidv4} = require('uuid'); +const { v4: uuidv4 } = require('uuid'); const PassportLocalStrategy = require('passport-local').Strategy; const autoBind = require('auto-bind'); -const {isEmail} = require('validator'); +const { isEmail } = require('validator'); // import swaggerUi from 'swagger-ui-express'; -passport.serializeUser(function ({id, email, email_verified, roles, provider, firstname, lastname}, done) { +passport.serializeUser(function( + { id, email, email_verified, roles, provider, firstname, lastname }, + done +) { done(null, { id, email, email_verified, provider, - firstname, lastname, + firstname, + lastname, roles: (roles || '') .split(',') - .reduce((obj, role) => ({...obj, [role]: true}), {}) - }) - ; + .reduce((obj, role) => ({ ...obj, [role]: true }), {}) + }); }); -passport.deserializeUser(function (user, done) { +passport.deserializeUser(function(user, done) { done(null, user); }); - export default class GqlAuthResolver { - private app: Noco; private dbDriver: Knex; @@ -48,58 +49,60 @@ export default class GqlAuthResolver { private jwtOptions: any; - - constructor(app: Noco, dbDriver: XKnex, connectionConfig: DbConfig, config: NcConfig) { + constructor( + app: Noco, + dbDriver: XKnex, + connectionConfig: DbConfig, + config: NcConfig + ) { this.app = app; this.dbDriver = dbDriver; this.connectionConfig = connectionConfig; this.config = config; autoBind(this); - this.jwtOptions = {} + this.jwtOptions = {}; this.jwtOptions.jwtFromRequest = ExtractJwt.fromHeader('xc-auth'); this.jwtOptions.secretOrKey = this.config?.auth?.jwt?.secret ?? 'secret'; if (this.config?.auth?.jwt?.options) { Object.assign(this.jwtOptions, this.config?.auth?.jwt?.options); } - } get users() { - return this.dbDriver('xc_users') + return this.dbDriver('xc_users'); } public async init() { - await this.emailClient?.init(); await this.createTableIfNotExist(); this.initStrategies(); - this.app.router.use(passport.initialize()) + this.app.router.use(passport.initialize()); const apiPrefix = this.connectionConfig?.meta?.api?.prefix || 'v1'; - this.app.router.get('/password/reset/:token', function (req, res) { + this.app.router.get('/password/reset/:token', function(req, res) { res.render(__dirname + '/auth/resetPassword', { token: JSON.stringify(req.params?.token), baseUrl: `/api/${apiPrefix}/` }); }); - this.app.router.get('/email/verify/:token', function (req, res) { + this.app.router.get('/email/verify/:token', function(req, res) { res.render(__dirname + '/auth/emailVerify', { token: JSON.stringify(req.params?.token), baseUrl: `/api/${apiPrefix}/` }); }); - this.app.router.get('/signin', function (_req, res) { + this.app.router.get('/signin', function(_req, res) { res.render(__dirname + '/auth/signin', { baseUrl: `/api/${apiPrefix}/` }); }); - this.app.router.get('/signup', function (_req, res) { + this.app.router.get('/signup', function(_req, res) { res.render(__dirname + '/auth/signup', { baseUrl: `/api/${apiPrefix}/` }); @@ -107,65 +110,76 @@ export default class GqlAuthResolver { this.app.router.use(async (req, res, next) => { const user = await new Promise(resolve => { - passport.authenticate('jwt', {session: false}, (_err, user, _info) => { - if (user) { - return resolve(user); + passport.authenticate( + 'jwt', + { session: false }, + (_err, user, _info) => { + if (user) { + return resolve(user); + } + resolve({ roles: 'guest' }); } - resolve({roles: 'guest'}) - })(req, res, next); - }) + )(req, res, next); + }); await promisify((req as any).login.bind(req))(user); next(); }); - } - public initStrategies() { - const self = this; - passport.use(new Strategy(this.jwtOptions, (jwt_payload, done) => { - this.users.where({ - email: jwt_payload?.email - }).first().then(user => { - if (user) { - return done(null, user); - } else { - return done(new Error('User not found')); - } - }).catch(err => { - return done(err); + passport.use( + new Strategy(this.jwtOptions, (jwt_payload, done) => { + this.users + .where({ + email: jwt_payload?.email + }) + .first() + .then(user => { + if (user) { + return done(null, user); + } else { + return done(new Error('User not found')); + } + }) + .catch(err => { + return done(err); + }); }) - })); - - - passport.use(new PassportLocalStrategy({ - usernameField: 'email', - session: false - }, async function (email, password, done) { - try { - const user = await self.users.where({email}).first(); - if (!user) { - return done({msg: `Email ${email} is not registered!`}); - } + ); + + passport.use( + new PassportLocalStrategy( + { + usernameField: 'email', + session: false + }, + async function(email, password, done) { + try { + const user = await self.users.where({ email }).first(); + if (!user) { + return done({ msg: `Email ${email} is not registered!` }); + } - const hashedPassword = await promisify(bcrypt.hash)(password, user.salt); - if (user.password !== hashedPassword) { - return done({msg: `Password not valid!`}); - } else { - return done(null, user); + const hashedPassword = await promisify(bcrypt.hash)( + password, + user.salt + ); + if (user.password !== hashedPassword) { + return done({ msg: `Password not valid!` }); + } else { + return done(null, user); + } + } catch (e) { + done(e); } - } catch (e) { - done(e); } - } - )); - + ) + ); } - public getResolvers() { return { mapResolvers: () => ({ @@ -178,65 +192,68 @@ export default class GqlAuthResolver { TokenVerify: this.tokenValidate, ChangePassword: this.passwordChange }) - } + }; } public getSchema() { return authSchema; } - - public async signin(args, {req, res, next}) { + public async signin(args, { req, res, next }) { req.body = args.data; return new Promise((resolve, reject) => { - passport.authenticate('local', {session: false}, async (err, user, info): Promise => { - - try { - - if (!user || !user.email) { - if (err) { - return reject(err) - } - if (info) { - return reject(info) + passport.authenticate( + 'local', + { session: false }, + async (err, user, info): Promise => { + try { + if (!user || !user.email) { + if (err) { + return reject(err); + } + if (info) { + return reject(info); + } + return reject({ msg: 'Your signin has failed' }); } - return reject({msg: 'Your signin has failed'}); - } - - await promisify((req as any).login.bind(req))(user); - resolve({ - token: jwt.sign({ - email: user.email, - firstname: user.firstname, - lastname: user.lastname, - id: user.id, - roles: user.roles - }, this.jwtOptions.secretOrKey, this.config?.auth?.jwt?.options) - }); - } catch (e) { - console.log(e); - reject(e); + await promisify((req as any).login.bind(req))(user); + + resolve({ + token: jwt.sign( + { + email: user.email, + firstname: user.firstname, + lastname: user.lastname, + id: user.id, + roles: user.roles + }, + this.jwtOptions.secretOrKey, + this.config?.auth?.jwt?.options + ) + }); + } catch (e) { + console.log(e); + reject(e); + } } - - })(req, res, next); + )(req, res, next); }); - } - public async signup(args, {req}) { - - - const {email, firstname, lastname} = args.data; - let {password} = args.data; + public async signup(args, { req }) { + const { email, firstname, lastname } = args.data; + let { password } = args.data; if (!isEmail(email)) { throw new Error(`Not a valid email`); } - let user = await this.users.where({ - email - }).first(); + let user = await this.users + .where({ + email + }) + .first(); if (user) { throw new Error(`Email '${email}' already registered`); @@ -251,90 +268,102 @@ export default class GqlAuthResolver { const email_verification_token = uuidv4(); await this.users.insert({ - firstname, lastname, + firstname, + lastname, email, salt, password, email_verification_token }); - user = await this.users.where({ - email - }).first(); + user = await this.users + .where({ + email + }) + .first(); try { const template = (await import('./emailTemplate/verify')).default; await this.emailClient.mailSend({ to: email, - subject: "Verify email", + subject: 'Verify email', html: ejs.render(template, { verifyLink: `${req.ncSiteUrl}/email/verify/${user.email_verification_token}` }) - }) + }); } catch (e) { - console.log('Warning : `mailSend` failed, Please configure email configuration.'); + console.log( + 'Warning : `mailSend` failed, Please configure email configuration.' + ); } await promisify((req as any).login.bind(req))(user); user = (req as any).user; return { - token: jwt.sign({ - email: user.email, - firstname: user.firstname, - lastname: user.lastname, - id: user.id, - roles: user.roles - }, this.jwtOptions.secretOrKey, this.config?.auth?.jwt?.options) + token: jwt.sign( + { + email: user.email, + firstname: user.firstname, + lastname: user.lastname, + id: user.id, + roles: user.roles + }, + this.jwtOptions.secretOrKey, + this.config?.auth?.jwt?.options + ) }; } - public async passwordForgot(args, {req}) { + public async passwordForgot(args, { req }) { const email = args.email; if (!email) { - throw (new Error('Please enter your email address.')); + throw new Error('Please enter your email address.'); } - const user = await this.users.where({email}).first(); + const user = await this.users.where({ email }).first(); if (!user) { - throw (new Error('This email is not registered with us.')); + throw new Error('This email is not registered with us.'); } const token = uuidv4(); - await this.users.update({ - reset_password_token: token, - reset_password_expires: new Date(Date.now() + (60 * 60 * 1000)) - }).where({id: user.id}); + await this.users + .update({ + reset_password_token: token, + reset_password_expires: new Date(Date.now() + 60 * 60 * 1000) + }) + .where({ id: user.id }); try { const template = (await import('./emailTemplate/forgotPassword')).default; await this.emailClient.mailSend({ to: user.email, - subject: "Password Reset Link", + subject: 'Password Reset Link', text: `Visit following link to update your password : ${req.ncSiteUrl}/password/reset/${token}.`, html: ejs.render(template, { resetLink: `${req.ncSiteUrl}/password/reset/${token}` }) - }) - + }); } catch (e) { - console.log('Warning : `mailSend` failed, Please configure email configuration.'); + console.log( + 'Warning : `mailSend` failed, Please configure email configuration.' + ); } - console.log(`Password reset token : ${token}`) + console.log(`Password reset token : ${token}`); return true; - } public async tokenValidate(args) { - const token = args.tokenId; - const user = await this.users.where({reset_password_token: token}).first(); + const user = await this.users + .where({ reset_password_token: token }) + .first(); if (!user || !user.email) { throw new Error('Invalid token'); } @@ -345,97 +374,107 @@ export default class GqlAuthResolver { return true; } - public async passwordReset(args) { const token = args.tokenId; - const user = await this.users.where({reset_password_token: token}).first(); + const user = await this.users + .where({ reset_password_token: token }) + .first(); if (!user) { - throw (new Error('Invalid token')); + throw new Error('Invalid token'); } if (user.reset_password_expires < new Date()) { - throw (new Error('Password reset url expired')); + throw new Error('Password reset url expired'); } if (user.provider && user.provider !== 'local') { - throw (new Error('Email registered via social account')); + throw new Error('Email registered via social account'); } const salt = await promisify(bcrypt.genSalt)(10); const password = await promisify(bcrypt.hash)(args.password, salt); - await this.users.update({ - salt, password, - reset_password_expires: null, - reset_password_token: '' - }).where({ - id: user.id - }); + await this.users + .update({ + salt, + password, + reset_password_expires: null, + reset_password_token: '' + }) + .where({ + id: user.id + }); - return true + return true; } - - public async passwordChange(args, {req}): Promise { - - const {currentPassword, newPassword} = args; + public async passwordChange(args, { req }): Promise { + const { currentPassword, newPassword } = args; if (!req.isAuthenticated() || !req.user.id) { - throw new Error('Not authenticated') + throw new Error('Not authenticated'); } if (!currentPassword || !newPassword) { - throw new Error('Missing new/old password') + throw new Error('Missing new/old password'); } - const user = await this.users.where({email: req.user.email}).first(); - const hashedPassword = await promisify(bcrypt.hash)(currentPassword, user.salt); + const user = await this.users.where({ email: req.user.email }).first(); + const hashedPassword = await promisify(bcrypt.hash)( + currentPassword, + user.salt + ); if (hashedPassword !== user.password) { - throw new Error('Current password is wrong') + throw new Error('Current password is wrong'); } const salt = await promisify(bcrypt.genSalt)(10); const password = await promisify(bcrypt.hash)(newPassword, salt); - await this.users.update({ - salt, password - }).where({id: user.id}); + await this.users + .update({ + salt, + password + }) + .where({ id: user.id }); return true; } - public async emailVerification(args) { const token = args.tokenId; - const user = await this.users.where({email_verification_token: token}).first(); + const user = await this.users + .where({ email_verification_token: token }) + .first(); if (!user) { throw new Error('Invalid verification url'); } - await this.users.update({ - email_verification_token: '', - email_verified: true - }).where({id: user.id}); + await this.users + .update({ + email_verification_token: '', + email_verified: true + }) + .where({ id: user.id }); return true; } - - public async me(_args, {req}) { + public async me(_args, { req }) { return req?.user ?? {}; } public async updateUser(req, res) { - await this.users.update({ - firstname: req.body.firstname, - lastname: req.body.lastname, - }).where({ - id: req.user.id - }) - res.json({msg: 'Updated successfully'}); + await this.users + .update({ + firstname: req.body.firstname, + lastname: req.body.lastname + }) + .where({ + id: req.user.id + }); + res.json({ msg: 'Updated successfully' }); } - private async createTableIfNotExist() { - if (!(await this.dbDriver.schema.hasTable('xc_users'))) { - await this.dbDriver.schema.createTable('xc_users', function (table) { + await this.dbDriver.schema.createTable('xc_users', function(table) { table.increments(); table.string('email'); table.string('password', 255); @@ -450,15 +489,15 @@ export default class GqlAuthResolver { table.boolean('email_verified'); table.string('roles', 255).defaultTo('editor'); table.timestamps(); - }) + }); } } private get emailClient(): IEmailAdapter { return this.app?.metaMgr?.emailAdapter; } - -}/** +} +/** * @copyright Copyright (c) 2021, Xgene Cloud Ltd * * @author Naveen MR diff --git a/packages/nocodb/src/lib/noco/gql/GqlBaseResolver.ts b/packages/nocodb/src/lib/noco/gql/GqlBaseResolver.ts index f9b769e0f3..0476300825 100644 --- a/packages/nocodb/src/lib/noco/gql/GqlBaseResolver.ts +++ b/packages/nocodb/src/lib/noco/gql/GqlBaseResolver.ts @@ -1,9 +1,12 @@ export default class GqlBaseResolver { - constructor() { - } + constructor() {} // todo: type correction - public static applyMiddlewares(handlers: any[] = [], resolvers = {}, postHandlers: any[] = []): any { + public static applyMiddlewares( + handlers: any[] = [], + resolvers = {}, + postHandlers: any[] = [] + ): any { if (!handlers) { return resolvers; } @@ -11,31 +14,29 @@ export default class GqlBaseResolver { resolvers[name] = async (...args) => { try { for (const handler of handlers) { - await handler(...args) + await handler(...args); } const result = await (resolver as any)(...args); if (postHandlers) { for (const handler of postHandlers) { - await handler(result, ...args) + await handler(result, ...args); } } return result; } catch (e) { throw e; } - } + }; } return resolvers; } - protected generateResolverFromStringBody(fnBody: string[]): any { - if (!(fnBody && Array.isArray(fnBody) && fnBody.length)) { return; } // @ts-ignore - let handler = (args) => { + let handler = args => { return null; }; @@ -53,9 +54,8 @@ export default class GqlBaseResolver { // tslint:disable-next-line:no-eval handler = eval(js); // console.timeEnd('startTrans') - } catch (e) { - console.log('Error in transpilation', e) + console.log('Error in transpilation', e); } // tslint:disable-next-line:no-eval diff --git a/packages/nocodb/src/lib/noco/gql/GqlCommonResolvers.ts b/packages/nocodb/src/lib/noco/gql/GqlCommonResolvers.ts index 2f96160243..ee02396359 100644 --- a/packages/nocodb/src/lib/noco/gql/GqlCommonResolvers.ts +++ b/packages/nocodb/src/lib/noco/gql/GqlCommonResolvers.ts @@ -1,16 +1,23 @@ -import {BaseModelSql} from "../../dataMapper"; +import { BaseModelSql } from '../../dataMapper'; -export const m2mNotChildren = ({models = {}}: { models: { [key: string]: BaseModelSql } }) => { - return async (args) => { +export const m2mNotChildren = ({ + models = {} +}: { + models: { [key: string]: BaseModelSql }; +}) => { + return async args => { return models[args?.parent]?.m2mNotChildren(args); - } -} -export const m2mNotChildrenCount = ({models = {}}: { models: { [key: string]: BaseModelSql } }) => { - return async (args) => { + }; +}; +export const m2mNotChildrenCount = ({ + models = {} +}: { + models: { [key: string]: BaseModelSql }; +}) => { + return async args => { return models[args?.parent]?.m2mNotChildrenCount(args); - } -} - + }; +}; /** * @copyright Copyright (c) 2021, Xgene Cloud Ltd diff --git a/packages/nocodb/src/lib/noco/gql/GqlMiddleware.ts b/packages/nocodb/src/lib/noco/gql/GqlMiddleware.ts index 405b9326c3..bcc0923293 100644 --- a/packages/nocodb/src/lib/noco/gql/GqlMiddleware.ts +++ b/packages/nocodb/src/lib/noco/gql/GqlMiddleware.ts @@ -1,7 +1,7 @@ import autoBind from 'auto-bind'; -import Handlebars from "handlebars"; +import Handlebars from 'handlebars'; -import {Acls} from "../../../interface/config"; +import { Acls } from '../../../interface/config'; export default class GqlMiddleware { private tn: any; @@ -9,7 +9,6 @@ export default class GqlMiddleware { private models: any; constructor(acls: Acls, tn: string, middleWareBody?: string, models?: any) { - autoBind(this); this.acls = acls; this.tn = tn; @@ -18,7 +17,7 @@ export default class GqlMiddleware { if (middleWareBody) { Object.defineProperty(this, 'middleware', { value: this.generateResolverFromStringBody(middleWareBody) - }) + }); } } @@ -27,12 +26,11 @@ export default class GqlMiddleware { } private generateResolverFromStringBody(fnBody: string): any { - if (!(fnBody && fnBody.length)) { return; } // @ts-ignore - let handler = (args) => { + let handler = args => { return null; }; @@ -41,40 +39,38 @@ export default class GqlMiddleware { // tslint:disable-next-line:no-eval handler = eval(js); } catch (e) { - console.log('Error in GQL Middleware transpilation', e) + console.log('Error in GQL Middleware transpilation', e); } return handler; } // @ts-ignore - public async middleware(_args, {req, res, next}, info: any): Promise { - - const replaceEnvVarRec = (obj) => { - + public async middleware(_args, { req, res, next }, info: any): Promise { + const replaceEnvVarRec = obj => { return JSON.parse(JSON.stringify(obj), (_key, value) => { - return typeof value === 'string' ? Handlebars.compile(value, {noEscape: true})({ - req - // : { - // user: {id: 1} // (req as any).user - // } - }) : value; + return typeof value === 'string' + ? Handlebars.compile(value, { noEscape: true })({ + req + // : { + // user: {id: 1} // (req as any).user + // } + }) + : value; }); - } - + }; const getOperation = (operation, fieldName) => { if (operation === 'mutation') { if (fieldName.endsWith('Create')) { - return 'create' + return 'create'; } else if (fieldName.endsWith('Update')) { - return 'update' + return 'update'; } else if (fieldName.endsWith('Delete')) { - return 'delete' + return 'delete'; } } return 'read'; - } - + }; const roleOperationPossible = (roles, operation, object) => { res.locals.xcAcl = null; @@ -90,7 +86,10 @@ export default class GqlMiddleware { if (this.acl[roleName][operation]) { return true; } - } else if (this.acl?.[roleName]?.[operation] && roleOperationObjectGet(roleName, operation, object)) { + } else if ( + this.acl?.[roleName]?.[operation] && + roleOperationObjectGet(roleName, operation, object) + ) { return true; } } catch (e) { @@ -98,32 +97,39 @@ export default class GqlMiddleware { } } if (errors?.length) { - throw errors[0] + throw errors[0]; } return false; - } + }; // @ts-ignore const roleOperationObjectGet = (role, operation, object) => { const columns = this.acl[role][operation].columns; if (columns) { // todo: merge allowed columns if multiple roles - const allowedCols = Object.keys(columns).filter(col => columns[col]) - res.locals.xcAcl = {allowedCols, operation, columns}; + const allowedCols = Object.keys(columns).filter(col => columns[col]); + res.locals.xcAcl = { allowedCols, operation, columns }; - if (info.fieldName.endsWith('Update') || info.fieldName.endsWith('Create')) { + if ( + info.fieldName.endsWith('Update') || + info.fieldName.endsWith('Create') + ) { if (Array.isArray(object)) { for (const row of object) { for (const colInReq of Object.keys(row)) { if (!allowedCols.includes(colInReq)) { - throw new Error(`User doesn't have permission to add/edit '${colInReq}' column`); + throw new Error( + `User doesn't have permission to add/edit '${colInReq}' column` + ); } } } } else { for (const colInReq of Object.keys(object)) { if (!allowedCols.includes(colInReq)) { - throw new Error(`User doesn't have permission to edit '${colInReq}' column`); + throw new Error( + `User doesn't have permission to edit '${colInReq}' column` + ); } } } @@ -131,25 +137,34 @@ export default class GqlMiddleware { } else { if (this.acl?.[role]?.[operation]?.custom) { if (this.acl?.[role]?.[operation]?.custom) { - const condition = replaceEnvVarRec(this.acl?.[role]?.[operation]?.custom) - _args.conditionGraph = {condition, models: this.models}; + const condition = replaceEnvVarRec( + this.acl?.[role]?.[operation]?.custom + ); + _args.conditionGraph = { condition, models: this.models }; } } return Object.values(columns).some(Boolean); } } - } - - const roles = (req as any)?.locals?.user?.roles ?? (req as any)?.session?.passport?.user?.roles ?? { - guest: true }; + const roles = (req as any)?.locals?.user?.roles ?? + (req as any)?.session?.passport?.user?.roles ?? { + guest: true + }; + try { - const allowed = roleOperationPossible(roles, getOperation(info.operation.operation, info.fieldName), _args?.data); + const allowed = roleOperationPossible( + roles, + getOperation(info.operation.operation, info.fieldName), + _args?.data + ); if (allowed) { return; } else { - const msg = roles.guest ? `Access Denied : Please Login or Signup for a new account` : `Access Denied for this account`; + const msg = roles.guest + ? `Access Denied : Please Login or Signup for a new account` + : `Access Denied for this account`; throw new Error(msg); } } catch (e) { @@ -158,13 +173,13 @@ export default class GqlMiddleware { } // @ts-ignore - public async postMiddleware(data, args, {req, res}, info): Promise { + public async postMiddleware(data, args, { req, res }, info): Promise { if (!res.locals.xcAcl) { return data; } // @ts-ignore - const {allowedCols, operation, columns} = res.locals.xcAcl; + const { allowedCols, operation, columns } = res.locals.xcAcl; if (!columns) { return data; @@ -201,18 +216,16 @@ export default class GqlMiddleware { delete data[colInReq]; } } - } - return data; } public async postLoaderMiddleware(...args): Promise { - return this.postMiddleware(args[0], args[2], args[3], args[4]) + return this.postMiddleware(args[0], args[2], args[3], args[4]); } - -}/** +} +/** * @copyright Copyright (c) 2021, Xgene Cloud Ltd * * @author Naveen MR diff --git a/packages/nocodb/src/lib/noco/gql/GqlProcedureResolver.ts b/packages/nocodb/src/lib/noco/gql/GqlProcedureResolver.ts index d52c85dc7a..861b67cc58 100644 --- a/packages/nocodb/src/lib/noco/gql/GqlProcedureResolver.ts +++ b/packages/nocodb/src/lib/noco/gql/GqlProcedureResolver.ts @@ -1,17 +1,20 @@ import autoBind from 'auto-bind'; -import BaseProcedure from "../common/BaseProcedure"; -import XcProcedure from "../common/XcProcedure"; +import BaseProcedure from '../common/BaseProcedure'; +import XcProcedure from '../common/XcProcedure'; -import {GqlApiBuilder} from "./GqlApiBuilder"; -import GqlBaseResolver from "./GqlBaseResolver"; - -export class GqlProcedureResolver - extends BaseProcedure { +import { GqlApiBuilder } from './GqlApiBuilder'; +import GqlBaseResolver from './GqlBaseResolver'; +export class GqlProcedureResolver extends BaseProcedure { private acls: { [aclName: string]: { [role: string]: boolean } }; - constructor(builder: GqlApiBuilder, functions: any[], procedures: any[], acls) { + constructor( + builder: GqlApiBuilder, + functions: any[], + procedures: any[], + acls + ) { super(); autoBind(this); this.builder = builder; @@ -22,90 +25,90 @@ export class GqlProcedureResolver } public fnHandler(name) { - return (async (args) => { + return (async args => { let body = []; try { body = JSON.parse(args.body); - } catch (_e) { - } + } catch (_e) {} const result = await this.xcProcedure.callFunction(name, body); return JSON.stringify(result, null, 2); - }).bind(this) + }).bind(this); } private procHandler(name) { // @ts-ignore - return (async (args) => { + return (async args => { let body = []; try { body = JSON.parse(args.body); - } catch (_e) { - } + } catch (_e) {} const result = await this.xcProcedure.callProcedure(name, body); return JSON.stringify(result, null, 2); - }).bind(this) + }).bind(this); } public mapResolvers(): any { const resolvers = {}; if (this.functions) { - for (const {function_name} of this.functions) { + for (const { function_name } of this.functions) { resolvers[`_${function_name}`] = this.fnHandler(function_name); } } if (this.procedures) { - for (const {procedure_name} of this.procedures) { + for (const { procedure_name } of this.procedures) { resolvers[`_${procedure_name}`] = this.procHandler(procedure_name); } } return GqlBaseResolver.applyMiddlewares([this.middleware], resolvers); - } public getSchema() { if (!this.functions?.length && !this.procedures?.length) { - return '' + return ''; } let resolvers = ` type Mutation { `; if (this.functions) { - for (const {function_name} of this.functions) { + for (const { function_name } of this.functions) { resolvers += `_${function_name}(body:String):String\r\n`; } } if (this.procedures) { - for (const {procedure_name} of this.procedures) { + for (const { procedure_name } of this.procedures) { resolvers += `_${procedure_name}(body:String):String\r\n`; } } return resolvers + `}\r\n`; } - updateMiddlewareBody(_body: string) { } + updateMiddlewareBody(_body: string) {} - private async middleware(_args, {req}, info: any) { + private async middleware(_args, { req }, info: any) { const roles = (req as any)?.session?.passport?.user?.roles ?? { guest: true }; try { - const allowed = Object.keys(roles).some(role => roles[role] && this.acls?.[info.fieldName.slice(1)]?.[role]); + const allowed = Object.keys(roles).some( + role => roles[role] && this.acls?.[info.fieldName.slice(1)]?.[role] + ); if (allowed) { // any additional rules can be made here return; } else { - const msg = roles.guest ? `Access Denied : Please Login or Signup for a new account` : `Access Denied for this account`; + const msg = roles.guest + ? `Access Denied : Please Login or Signup for a new account` + : `Access Denied for this account`; throw new Error(msg); } } catch (e) { - throw e + throw e; } } } - /** * @copyright Copyright (c) 2021, Xgene Cloud Ltd * diff --git a/packages/nocodb/src/lib/noco/gql/GqlResolver.ts b/packages/nocodb/src/lib/noco/gql/GqlResolver.ts index bfe549d1db..95dde43251 100644 --- a/packages/nocodb/src/lib/noco/gql/GqlResolver.ts +++ b/packages/nocodb/src/lib/noco/gql/GqlResolver.ts @@ -1,30 +1,36 @@ import autoBind from 'auto-bind'; -import {Acls} from "../../../interface/config"; -import {BaseModelSql} from "../../dataMapper"; -import Noco from "../Noco"; +import { Acls } from '../../../interface/config'; +import { BaseModelSql } from '../../dataMapper'; +import Noco from '../Noco'; -import GqlBaseResolver from "./GqlBaseResolver"; -import GqlMiddleware from "./GqlMiddleware"; +import GqlBaseResolver from './GqlBaseResolver'; +import GqlMiddleware from './GqlMiddleware'; function parseHrtimeToSeconds(hrtime) { - const seconds = (hrtime[0] + (hrtime[1] / 1e6)).toFixed(3); + const seconds = (hrtime[0] + hrtime[1] / 1e6).toFixed(3); return seconds; } export default class GqlResolver extends GqlBaseResolver { - // @ts-ignore private app: Noco; private models: { [key: string]: BaseModelSql }; private table: string; - private typeClass: new(obj: any) => any; + private typeClass: new (obj: any) => any; private acls: Acls; private functions: { [key: string]: any }; private middlewareStringBody?: string; - - constructor(app: Noco, models: { [key: string]: BaseModelSql }, table: string, typeClass: { new(obj: any): any }, acls: Acls, functions: { [key: string]: string[] }, middlewareStringBody?: string) { + constructor( + app: Noco, + models: { [key: string]: BaseModelSql }, + table: string, + typeClass: { new (obj: any): any }, + acls: Acls, + functions: { [key: string]: string[] }, + middlewareStringBody?: string + ) { super(); autoBind(this); this.app = app; @@ -36,19 +42,24 @@ export default class GqlResolver extends GqlBaseResolver { this.middlewareStringBody = middlewareStringBody; } - private get model(): BaseModelSql { return this.models?.[this.table]; } - public async list(args, {req, res}: { req: any & { model: BaseModelSql }, res: any }): Promise { + public async list( + args, + { req, res }: { req: any & { model: BaseModelSql }; res: any } + ): Promise { const startTime = process.hrtime(); try { if (args.conditionGraph && typeof args.conditionGraph === 'string') { - args.conditionGraph = {models: this.models, condition: JSON.parse(args.conditionGraph)} + args.conditionGraph = { + models: this.models, + condition: JSON.parse(args.conditionGraph) + }; } if (args.condition && typeof args.condition === 'string') { - args.condition = JSON.parse(args.condition) + args.condition = JSON.parse(args.condition); } } catch (e) { /* ignore parse error */ @@ -57,63 +68,66 @@ export default class GqlResolver extends GqlBaseResolver { const elapsedSeconds = parseHrtimeToSeconds(process.hrtime(startTime)); res.setHeader('xc-db-response', elapsedSeconds); - return (data).map(o => { + return data.map(o => { return new req.gqlType(o); }); } - public async create(args, {req}): Promise { + public async create(args, { req }): Promise { const data = await req.model.insert(args.data, null, req); return new req.gqlType(data); } - public async read(args, {req}): Promise { + public async read(args, { req }): Promise { const data = await req.model.readByPk(args.id, args); return new req.gqlType(data); } - public async update(args, {req}): Promise { + public async update(args, { req }): Promise { const data = await req.model.updateByPk(args.id, args.data, null, req); return data; } - public async delete(args, {req}): Promise { + public async delete(args, { req }): Promise { const data = await req.model.delByPk(args.id, null, req); return data; } - public async exists(args, {req}): Promise { + public async exists(args, { req }): Promise { const data = await req.model.exists(args.id, args); return data; } - public async findOne(args, {req}): Promise { + public async findOne(args, { req }): Promise { const data = await req.model.findOne(args); return new req.gqlType(data); } - public async groupBy(args, {req}): Promise { + public async groupBy(args, { req }): Promise { const data = await req.model.groupBy(args); return data; } - public async aggregate(args, {req}): Promise { + public async aggregate(args, { req }): Promise { const data = await req.model.aggregate(args); return data; } - public async distinct(args, {req}): Promise { + public async distinct(args, { req }): Promise { const data = (await req.model.distinct(args)).map(d => new req.gqlType(d)); return data; } - public async count(args, {req}): Promise { + public async count(args, { req }): Promise { try { if (args.conditionGraph && typeof args.conditionGraph === 'string') { - args.conditionGraph = {models: this.models, condition: JSON.parse(args.conditionGraph)} + args.conditionGraph = { + models: this.models, + condition: JSON.parse(args.conditionGraph) + }; } if (args.condition && typeof args.condition === 'string') { - args.condition = JSON.parse(args.condition) + args.condition = JSON.parse(args.condition); } } catch (e) { /* ignore parse error */ @@ -122,62 +136,145 @@ export default class GqlResolver extends GqlBaseResolver { return data.count; } - public async distribution(args, {req}): Promise { + public async distribution(args, { req }): Promise { const data = await req.model.distribution(args); return data; } - public async createb(args, {req}): Promise { + public async createb(args, { req }): Promise { const data = await req.model.insertb(args.data); return data; } - public async updateb(args, {req}): Promise { + public async updateb(args, { req }): Promise { const data = await req.model.updateb(args.data); return data; } - public async deleteb(args, {req}): Promise { + public async deleteb(args, { req }): Promise { const data = await req.model.delb(args.data); return data; } - public updateMiddlewareBody(body: string): this { this.middlewareStringBody = body; return this; } public mapResolvers(customResolver: any): any { - const mw = new GqlMiddleware(this.acls, this.table, this.middlewareStringBody, this.models); + const mw = new GqlMiddleware( + this.acls, + this.table, + this.middlewareStringBody, + this.models + ); // todo: replace with inflection const name = this.model._tn; - return GqlResolver.applyMiddlewares([(_, {req}) => { - req.models = this.models; - req.model = this.model; - req.gqlType = this.typeClass; - }, mw.middleware], { - - ...(customResolver?.additional?.[this.table] || {}), - - [`${name}List`]: customResolver?.override?.[`${name}List`] || this.generateResolverFromStringBody(this.functions[`${name}List`]) || this.list, - [`${name}FindOne`]: customResolver?.override?.[`${name}FindOne`] || this.generateResolverFromStringBody(this.functions[`${name}FindOne`]) || this.findOne, - [`${name}Count`]: customResolver?.override?.[`${name}Count`] || this.generateResolverFromStringBody(this.functions[`${name}Count`]) || this.count, - [`${name}Distinct`]: customResolver?.override?.[`${name}Distinct`] || this.generateResolverFromStringBody(this.functions[`${name}Distinct`]) || this.distinct, - [`${name}GroupBy`]: customResolver?.override?.[`${name}GroupBy`] || this.generateResolverFromStringBody(this.functions[`${name}GroupBy`]) || this.groupBy, - [`${name}Aggregate`]: customResolver?.override?.[`${name}Aggregate`] || this.generateResolverFromStringBody(this.functions[`${name}Aggregate`]) || this.aggregate, - [`${name}Distribution`]: customResolver?.override?.[`${name}Distribution`] || this.generateResolverFromStringBody(this.functions[`${name}Distribution`]) || this.distribution, - ...(this.model.type === 'table' ? { - [`${name}Read`]: customResolver?.override?.[`${name}Read`] || this.generateResolverFromStringBody(this.functions[`${name}Read`]) || this.read, - [`${name}Exists`]: customResolver?.override?.[`${name}Exists`] || this.generateResolverFromStringBody(this.functions[`${name}Exists`]) || this.exists, - [`${name}Create`]: customResolver?.override?.[`${name}Create`] || this.generateResolverFromStringBody(this.functions[`${name}Create`]) || this.create, - [`${name}Update`]: customResolver?.override?.[`${name}Update`] || this.generateResolverFromStringBody(this.functions[`${name}Update`]) || this.update, - [`${name}Delete`]: customResolver?.override?.[`${name}Delete`] || this.generateResolverFromStringBody(this.functions[`${name}Delete`]) || this.delete, - [`${name}CreateBulk`]: customResolver?.override?.[`${name}CreateBulk`] || this.generateResolverFromStringBody(this.functions[`${name}CreateBulk`]) || this.createb, - [`${name}UpdateBulk`]: customResolver?.override?.[`${name}UpdateBulk`] || this.generateResolverFromStringBody(this.functions[`${name}UpdateBulk`]) || this.updateb, - [`${name}DeleteBulk`]: customResolver?.override?.[`${name}DeleteBulk`] || this.generateResolverFromStringBody(this.functions[`${name}DeleteBulk`]) || this.deleteb, - } : {}) - }, [mw.postMiddleware]); + return GqlResolver.applyMiddlewares( + [ + (_, { req }) => { + req.models = this.models; + req.model = this.model; + req.gqlType = this.typeClass; + }, + mw.middleware + ], + { + ...(customResolver?.additional?.[this.table] || {}), + + [`${name}List`]: + customResolver?.override?.[`${name}List`] || + this.generateResolverFromStringBody(this.functions[`${name}List`]) || + this.list, + [`${name}FindOne`]: + customResolver?.override?.[`${name}FindOne`] || + this.generateResolverFromStringBody( + this.functions[`${name}FindOne`] + ) || + this.findOne, + [`${name}Count`]: + customResolver?.override?.[`${name}Count`] || + this.generateResolverFromStringBody(this.functions[`${name}Count`]) || + this.count, + [`${name}Distinct`]: + customResolver?.override?.[`${name}Distinct`] || + this.generateResolverFromStringBody( + this.functions[`${name}Distinct`] + ) || + this.distinct, + [`${name}GroupBy`]: + customResolver?.override?.[`${name}GroupBy`] || + this.generateResolverFromStringBody( + this.functions[`${name}GroupBy`] + ) || + this.groupBy, + [`${name}Aggregate`]: + customResolver?.override?.[`${name}Aggregate`] || + this.generateResolverFromStringBody( + this.functions[`${name}Aggregate`] + ) || + this.aggregate, + [`${name}Distribution`]: + customResolver?.override?.[`${name}Distribution`] || + this.generateResolverFromStringBody( + this.functions[`${name}Distribution`] + ) || + this.distribution, + ...(this.model.type === 'table' + ? { + [`${name}Read`]: + customResolver?.override?.[`${name}Read`] || + this.generateResolverFromStringBody( + this.functions[`${name}Read`] + ) || + this.read, + [`${name}Exists`]: + customResolver?.override?.[`${name}Exists`] || + this.generateResolverFromStringBody( + this.functions[`${name}Exists`] + ) || + this.exists, + [`${name}Create`]: + customResolver?.override?.[`${name}Create`] || + this.generateResolverFromStringBody( + this.functions[`${name}Create`] + ) || + this.create, + [`${name}Update`]: + customResolver?.override?.[`${name}Update`] || + this.generateResolverFromStringBody( + this.functions[`${name}Update`] + ) || + this.update, + [`${name}Delete`]: + customResolver?.override?.[`${name}Delete`] || + this.generateResolverFromStringBody( + this.functions[`${name}Delete`] + ) || + this.delete, + [`${name}CreateBulk`]: + customResolver?.override?.[`${name}CreateBulk`] || + this.generateResolverFromStringBody( + this.functions[`${name}CreateBulk`] + ) || + this.createb, + [`${name}UpdateBulk`]: + customResolver?.override?.[`${name}UpdateBulk`] || + this.generateResolverFromStringBody( + this.functions[`${name}UpdateBulk`] + ) || + this.updateb, + [`${name}DeleteBulk`]: + customResolver?.override?.[`${name}DeleteBulk`] || + this.generateResolverFromStringBody( + this.functions[`${name}DeleteBulk`] + ) || + this.deleteb + } + : {}) + }, + [mw.postMiddleware] + ); } } /** diff --git a/packages/nocodb/src/lib/noco/gql/auth/schema.ts b/packages/nocodb/src/lib/noco/gql/auth/schema.ts index 751cb1dcc2..55dc0e65d4 100644 --- a/packages/nocodb/src/lib/noco/gql/auth/schema.ts +++ b/packages/nocodb/src/lib/noco/gql/auth/schema.ts @@ -41,7 +41,8 @@ type User { type XcToken{ token: String } -`/** +`; +/** * @copyright Copyright (c) 2021, Xgene Cloud Ltd * * @author Naveen MR diff --git a/packages/nocodb/src/lib/noco/gql/common.schema.ts b/packages/nocodb/src/lib/noco/gql/common.schema.ts index d071036445..ea12584479 100644 --- a/packages/nocodb/src/lib/noco/gql/common.schema.ts +++ b/packages/nocodb/src/lib/noco/gql/common.schema.ts @@ -49,7 +49,8 @@ type Query{ -`/** +`; +/** * @copyright Copyright (c) 2021, Xgene Cloud Ltd * * @author Naveen MR diff --git a/packages/nocodb/src/lib/noco/gql/emailTemplate/forgotPassword.ts b/packages/nocodb/src/lib/noco/gql/emailTemplate/forgotPassword.ts index 615bf74e32..f59509132d 100644 --- a/packages/nocodb/src/lib/noco/gql/emailTemplate/forgotPassword.ts +++ b/packages/nocodb/src/lib/noco/gql/emailTemplate/forgotPassword.ts @@ -169,7 +169,7 @@ table[class=body] .article { -` +`; /** * @copyright Copyright (c) 2021, Xgene Cloud Ltd * diff --git a/packages/nocodb/src/lib/noco/gql/emailTemplate/verify.ts b/packages/nocodb/src/lib/noco/gql/emailTemplate/verify.ts index 5626709c98..673dd8f8b6 100644 --- a/packages/nocodb/src/lib/noco/gql/emailTemplate/verify.ts +++ b/packages/nocodb/src/lib/noco/gql/emailTemplate/verify.ts @@ -206,7 +206,7 @@ export default ` -` +`; /** * @copyright Copyright (c) 2021, Xgene Cloud Ltd diff --git a/packages/nocodb/src/lib/noco/meta/MetaAPILogger.ts b/packages/nocodb/src/lib/noco/meta/MetaAPILogger.ts index 10ead5cc32..a7bc36ce12 100644 --- a/packages/nocodb/src/lib/noco/meta/MetaAPILogger.ts +++ b/packages/nocodb/src/lib/noco/meta/MetaAPILogger.ts @@ -1,8 +1,7 @@ -import {XKnex} from "../../dataMapper"; -import {Request} from 'express'; +import { XKnex } from '../../dataMapper'; +import { Request } from 'express'; export default class MetaAPILogger { - static _instance: MetaAPILogger; knex: XKnex; @@ -13,7 +12,7 @@ export default class MetaAPILogger { filename: 'noco_log.db' }, useNullAsDefault: true - }) + }); } async init() { @@ -23,29 +22,26 @@ export default class MetaAPILogger { }); } - static async mw(req, res, next) { - if (process.env.NC_LOGGER) { const oldWrite = res.write, oldEnd = res.end; const chunks = []; - res.write = function (chunk) { + res.write = function(chunk) { chunks.push(chunk); // eslint-disable-next-line prefer-rest-params oldWrite.apply(res, arguments); }; - res.end = function (chunk) { - if (chunk) - chunks.push(chunk); + res.end = function(chunk) { + if (chunk) chunks.push(chunk); const body = Buffer.concat(chunks).toString('utf8'); - MetaAPILogger.log(req, body) + MetaAPILogger.log(req, body); // eslint-disable-next-line prefer-rest-params oldEnd.apply(res, arguments); }; @@ -59,7 +55,7 @@ export default class MetaAPILogger { } if (!this._instance) { this._instance = new MetaAPILogger(); - await this._instance.init() + await this._instance.init(); } await this._instance.knex('nc_log').insert({ path: req.url, @@ -69,10 +65,8 @@ export default class MetaAPILogger { method: req.method, operation: req.body?.api, response: typeof res === 'string' ? res : JSON.stringify(res) - }) + }); } - - } class XcLoggerMigrationSource { @@ -81,7 +75,7 @@ class XcLoggerMigrationSource { // arguments to getMigrationName and getMigration public getMigrations(): Promise { // In this example we are just returning migration names - return Promise.resolve(['logger']) + return Promise.resolve(['logger']); } public getMigrationName(migration): string { @@ -104,10 +98,10 @@ class XcLoggerMigrationSource { table.text('response'); table.text('comments'); table.timestamps(true, true); - }) + }); }, async down(knex) { - await knex.schema.dropTable('nc_log') + await knex.schema.dropTable('nc_log'); } }; } diff --git a/packages/nocodb/src/lib/noco/meta/NcMetaIO.ts b/packages/nocodb/src/lib/noco/meta/NcMetaIO.ts index 6ba3253c26..ef2a3e77c6 100644 --- a/packages/nocodb/src/lib/noco/meta/NcMetaIO.ts +++ b/packages/nocodb/src/lib/noco/meta/NcMetaIO.ts @@ -1,26 +1,60 @@ -import {NcConfig} from "../../../interface/config"; -import Noco from "../Noco"; -import {XKnex} from "../../dataMapper"; +import { NcConfig } from '../../../interface/config'; +import Noco from '../Noco'; +import { XKnex } from '../../dataMapper'; const META_TABLES = { - graphql: ['nc_models', 'nc_resolvers', 'nc_loaders', 'nc_store', 'nc_hooks', 'nc_roles', 'nc_acl', 'nc_api_tokens', 'nc_relations', 'nc_migrations', + graphql: [ + 'nc_models', + 'nc_resolvers', + 'nc_loaders', + 'nc_store', + 'nc_hooks', + 'nc_roles', + 'nc_acl', + 'nc_api_tokens', + 'nc_relations', + 'nc_migrations', 'nc_disabled_models_for_role', - 'nc_shared_views', 'nc_cron', 'nc_audit' + 'nc_shared_views', + 'nc_cron', + 'nc_audit' + ], + grpc: [ + 'nc_models', + 'nc_rpc', + 'nc_store', + 'nc_hooks', + 'nc_roles', + 'nc_acl', + 'nc_relations', + 'nc_migrations', + 'nc_api_tokens', + 'nc_disabled_models_for_role', + 'nc_shared_views', + 'nc_cron' ], - grpc: ['nc_models', 'nc_rpc', 'nc_store', 'nc_hooks', 'nc_roles', 'nc_acl', 'nc_relations', 'nc_migrations', 'nc_api_tokens', 'nc_disabled_models_for_role', - 'nc_shared_views', 'nc_cron'], rest: [ - 'nc_models', 'nc_routes', 'nc_store', 'nc_hooks', 'nc_roles', 'nc_acl', 'nc_relations', 'nc_migrations', 'nc_api_tokens', + 'nc_models', + 'nc_routes', + 'nc_store', + 'nc_hooks', + 'nc_roles', + 'nc_acl', + 'nc_relations', + 'nc_migrations', + 'nc_api_tokens', 'nc_disabled_models_for_role', - 'nc_shared_views', 'nc_cron', 'nc_audit'], -} + 'nc_shared_views', + 'nc_cron', + 'nc_audit' + ] +}; export default abstract class NcMetaIO { - protected app: Noco; protected config: NcConfig; - public abstract get knexConnection(): XKnex ; + public abstract get knexConnection(): XKnex; constructor(app: Noco, config: NcConfig) { this.app = app; @@ -29,104 +63,135 @@ export default abstract class NcMetaIO { public abstract metaInit(): Promise; - public abstract metaInsert(project_id: string, - dbAlias: string, - target: string, - data: any) - : Promise; - - public abstract audit(project_id: string, - dbAlias: string, - target: string, - data: any) - : Promise; - - public abstract metaUpdate(project_id: string, - dbAlias: string, - target: string, - data: any, - idOrCondition: string | { [key: string]: any }, - xcCondition?: XcCondition) - : Promise; - - public abstract metaDelete(project_id: string, - dbAlias: string, - target: string, - idOrCondition?: string | { [key: string]: any }, - xcCondition?: XcCondition) - : Promise; - - public abstract metaDeleteAll(project_id: string, - dbAlias: string) - : Promise; - - public abstract metaGet(project_id: string, - dbAlias: string, - target: string, - idOrCondition: string | { [key: string]: any }, - fields?: string[], xcCondition?: XcCondition) - : Promise; - - public abstract metaList(project_id: string, - dbAlias: string, - target: string, - args?: { - condition?: { [key: string]: any }, - limit?: number, - offset?: number, - xcCondition?: XcCondition, - fields?: string[] - }): Promise; - - public abstract metaPaginatedList(project_id: string, - dbAlias: string, - target: string, - args?: { - condition?: { [key: string]: any }, - limit?: number, - offset?: number, - xcCondition?: XcCondition, - fields?: string[], - sort?: { field: string, desc?: boolean } - }): Promise<{ - list: any[], - count: number + public abstract metaInsert( + project_id: string, + dbAlias: string, + target: string, + data: any + ): Promise; + + public abstract audit( + project_id: string, + dbAlias: string, + target: string, + data: any + ): Promise; + + public abstract metaUpdate( + project_id: string, + dbAlias: string, + target: string, + data: any, + idOrCondition: string | { [key: string]: any }, + xcCondition?: XcCondition + ): Promise; + + public abstract metaDelete( + project_id: string, + dbAlias: string, + target: string, + idOrCondition?: string | { [key: string]: any }, + xcCondition?: XcCondition + ): Promise; + + public abstract metaDeleteAll( + project_id: string, + dbAlias: string + ): Promise; + + public abstract metaGet( + project_id: string, + dbAlias: string, + target: string, + idOrCondition: string | { [key: string]: any }, + fields?: string[], + xcCondition?: XcCondition + ): Promise; + + public abstract metaList( + project_id: string, + dbAlias: string, + target: string, + args?: { + condition?: { [key: string]: any }; + limit?: number; + offset?: number; + xcCondition?: XcCondition; + fields?: string[]; + } + ): Promise; + + public abstract metaPaginatedList( + project_id: string, + dbAlias: string, + target: string, + args?: { + condition?: { [key: string]: any }; + limit?: number; + offset?: number; + xcCondition?: XcCondition; + fields?: string[]; + sort?: { field: string; desc?: boolean }; + } + ): Promise<{ + list: any[]; + count: number; }>; - - public abstract isMetaDataExists(project_id: string, - dbAlias: string): Promise; - - public abstract metaReset(project_id: string, - dbAlias: string, apiType?: string): Promise; - - public abstract projectCreate(projectName: string, - config: any, - description?: string, - meta?:boolean): Promise; - - public abstract projectUpdate(projectId: string, - config: any): Promise; - - public abstract projectAddUser(projectId: string, - userId: any, roles: string): Promise; - - public abstract projectRemoveUser(projectId: string, - userId: any): Promise; - - public abstract projectStatusUpdate(projectName: string, - status: string): Promise; + public abstract isMetaDataExists( + project_id: string, + dbAlias: string + ): Promise; + + public abstract metaReset( + project_id: string, + dbAlias: string, + apiType?: string + ): Promise; + + public abstract projectCreate( + projectName: string, + config: any, + description?: string, + meta?: boolean + ): Promise; + + public abstract projectUpdate(projectId: string, config: any): Promise; + + public abstract projectAddUser( + projectId: string, + userId: any, + roles: string + ): Promise; + + public abstract projectRemoveUser( + projectId: string, + userId: any + ): Promise; + + public abstract projectStatusUpdate( + projectName: string, + status: string + ): Promise; public abstract projectList(): Promise; public abstract userProjectList(userId: any): Promise; - public abstract isUserHaveAccessToProject(projectId: string, - userId: any): Promise; + public abstract isUserHaveAccessToProject( + projectId: string, + userId: any + ): Promise; - public abstract projectGet(projectName: string, encrypt?: boolean): Promise; + public abstract projectGet( + projectName: string, + encrypt?: boolean + ): Promise; - public abstract projectGetById(projectId: string, encrypt?: boolean): Promise; + public abstract projectGetById( + projectId: string, + encrypt?: boolean + ): Promise; public abstract projectDelete(title: string): Promise; public abstract projectDeleteById(id: string): Promise; @@ -142,11 +207,19 @@ export default abstract class NcMetaIO { public setConfig(config: NcConfig) { this.config = config; } - } type XcConditionStr = { - [key in 'lt' | 'gt' | 'le' | 'ge' | 'like' | 'nlike' | 'eq' | 'in' | 'nin']: any; + [key in + | 'lt' + | 'gt' + | 'le' + | 'ge' + | 'like' + | 'nlike' + | 'eq' + | 'in' + | 'nin']: any; }; interface XcCondition { @@ -155,12 +228,10 @@ interface XcCondition { _not?: XcCondition; [key: string]: XcConditionStr | any; - } -export { - META_TABLES -}/** +export { META_TABLES }; +/** * @copyright Copyright (c) 2021, Xgene Cloud Ltd * * @author Naveen MR diff --git a/packages/nocodb/src/lib/noco/meta/NcMetaIOImpl.ts b/packages/nocodb/src/lib/noco/meta/NcMetaIOImpl.ts index 441d3952d8..18710d5b88 100644 --- a/packages/nocodb/src/lib/noco/meta/NcMetaIOImpl.ts +++ b/packages/nocodb/src/lib/noco/meta/NcMetaIOImpl.ts @@ -1,35 +1,42 @@ import CryptoJS from 'crypto-js'; -import {customAlphabet} from 'nanoid' +import { customAlphabet } from 'nanoid'; -import {NcConfig} from "../../../interface/config"; -import {Knex, XKnex} from "../../dataMapper"; -import Noco from "../Noco"; -import XcMigrationSource from "../common/XcMigrationSource"; +import { NcConfig } from '../../../interface/config'; +import { Knex, XKnex } from '../../dataMapper'; +import Noco from '../Noco'; +import XcMigrationSource from '../common/XcMigrationSource'; -import NcMetaIO, {META_TABLES} from "./NcMetaIO"; -import NcConnectionMgr from "../common/NcConnectionMgr"; - - -const nanoid = customAlphabet('1234567890abcdefghijklmnopqrstuvwxyz_', 4) +import NcMetaIO, { META_TABLES } from './NcMetaIO'; +import NcConnectionMgr from '../common/NcConnectionMgr'; +const nanoid = customAlphabet('1234567890abcdefghijklmnopqrstuvwxyz_', 4); export default class NcMetaIOImpl extends NcMetaIO { - - - public async metaPaginatedList(projectId: string, dbAlias: string, target: string, args?: { condition?: { [key: string]: any; }; limit?: number; offset?: number; xcCondition?; fields?: string[]; sort?: { field: string, desc?: boolean } }): Promise<{ list: any[]; count: number; }> { - const query = this.knexConnection(target) - const countQuery = this.knexConnection(target) + public async metaPaginatedList( + projectId: string, + dbAlias: string, + target: string, + args?: { + condition?: { [key: string]: any }; + limit?: number; + offset?: number; + xcCondition?; + fields?: string[]; + sort?: { field: string; desc?: boolean }; + } + ): Promise<{ list: any[]; count: number }> { + const query = this.knexConnection(target); + const countQuery = this.knexConnection(target); if (projectId !== null) { - query.where('project_id', projectId) - countQuery.where('project_id', projectId) + query.where('project_id', projectId); + countQuery.where('project_id', projectId); } if (dbAlias !== null) { query.where('db_alias', dbAlias); countQuery.where('db_alias', dbAlias); } - if (args?.condition) { query.where(args.condition); countQuery.where(args.condition); @@ -44,27 +51,25 @@ export default class NcMetaIOImpl extends NcMetaIO { query.offset(args.offset); } if (args?.xcCondition) { - (query as any).condition(args.xcCondition) - (countQuery as any).condition(args.xcCondition) + (query as any) + .condition(args.xcCondition)(countQuery as any) + .condition(args.xcCondition); } if (args?.fields?.length) { - query.select(...args.fields) + query.select(...args.fields); } - return { list: await query, - count: (Object.values(await countQuery.count().first()))?.[0] as any + count: Object.values(await countQuery.count().first())?.[0] as any }; } - private connection: XKnex; // todo: need to fix private trx: Knex.Transaction; - constructor(app: Noco, config: NcConfig) { super(app, config); @@ -75,9 +80,13 @@ export default class NcMetaIOImpl extends NcMetaIO { if (this.config?.meta?.db) { this.connection = XKnex(this.config?.meta?.db); } else { - let dbIndex = this.config.envs?.[this.config.workingEnv]?.db.findIndex(c => c.meta.dbAlias === this.config?.auth?.jwt?.dbAlias) + let dbIndex = this.config.envs?.[this.config.workingEnv]?.db.findIndex( + c => c.meta.dbAlias === this.config?.auth?.jwt?.dbAlias + ); dbIndex = dbIndex === -1 ? 0 : dbIndex; - this.connection = XKnex(this.config.envs?.[this.config.workingEnv]?.db[dbIndex] as any); + this.connection = XKnex( + this.config.envs?.[this.config.workingEnv]?.db[dbIndex] as any + ); } NcConnectionMgr.setXcMeta(this); } @@ -107,9 +116,8 @@ export default class NcMetaIOImpl extends NcMetaIO { ): Promise { const query = this.knexConnection(target); - if (project_id !== null) { - query.where('project_id', project_id) + query.where('project_id', project_id); } if (dbAlias !== null) { query.where('db_alias', dbAlias); @@ -122,31 +130,32 @@ export default class NcMetaIOImpl extends NcMetaIO { } if (xcCondition) { - query.condition(xcCondition, {}) + query.condition(xcCondition, {}); } return query.del(); } - public async metaGet(project_id: string, - dbAlias: string, - target: string, - idOrCondition: string | { [p: string]: any }, - fields?: string[], xcCondition?): Promise { + public async metaGet( + project_id: string, + dbAlias: string, + target: string, + idOrCondition: string | { [p: string]: any }, + fields?: string[], + xcCondition? + ): Promise { const query = this.knexConnection(target); - if (xcCondition) { - query.condition(xcCondition) + query.condition(xcCondition); } if (fields?.length) { - query.select(...fields) + query.select(...fields); } - if (project_id !== null) { - query.where('project_id', project_id) + query.where('project_id', project_id); } if (dbAlias !== null) { query.where('db_alias', dbAlias); @@ -161,36 +170,47 @@ export default class NcMetaIOImpl extends NcMetaIO { query.where(idOrCondition); } - // console.log(query.toQuery()) return query.first(); } - - public async metaInsert(project_id: string, dbAlias: string, target: string, data: any): Promise { + public async metaInsert( + project_id: string, + dbAlias: string, + target: string, + data: any + ): Promise { return this.knexConnection(target).insert({ - 'db_alias': dbAlias, + db_alias: dbAlias, project_id, created_at: this.knexConnection?.fn?.now(), - updated_at: this.knexConnection?.fn?.now(), ...data + updated_at: this.knexConnection?.fn?.now(), + ...data }); } - public async metaList(project_id: string, dbAlias: string, target: string, args?: { - condition?: { [p: string]: any }; limit?: number; offset?: number, xcCondition?, - fields?: string[] - }): Promise { - const query = this.knexConnection(target) + public async metaList( + project_id: string, + dbAlias: string, + target: string, + args?: { + condition?: { [p: string]: any }; + limit?: number; + offset?: number; + xcCondition?; + fields?: string[]; + } + ): Promise { + const query = this.knexConnection(target); if (project_id !== null) { - query.where('project_id', project_id) + query.where('project_id', project_id); } if (dbAlias !== null) { query.where('db_alias', dbAlias); } - if (args?.condition) { query.where(args.condition); } @@ -201,20 +221,27 @@ export default class NcMetaIOImpl extends NcMetaIO { query.offset(args.offset); } if (args?.xcCondition) { - (query as any).condition(args.xcCondition) + (query as any).condition(args.xcCondition); } if (args?.fields?.length) { - query.select(...args.fields) + query.select(...args.fields); } return query; } - public async metaUpdate(project_id: string, dbAlias: string, target: string, data: any, idOrCondition?: string | { [p: string]: any }, xcCondition?): Promise { + public async metaUpdate( + project_id: string, + dbAlias: string, + target: string, + data: any, + idOrCondition?: string | { [p: string]: any }, + xcCondition? + ): Promise { const query = this.knexConnection(target); if (project_id !== null) { - query.where('project_id', project_id) + query.where('project_id', project_id); } if (dbAlias !== null) { query.where('db_alias', dbAlias); @@ -222,23 +249,25 @@ export default class NcMetaIOImpl extends NcMetaIO { delete data.created_at; - query.update({...data, updated_at: this.knexConnection?.fn?.now()}); + query.update({ ...data, updated_at: this.knexConnection?.fn?.now() }); if (typeof idOrCondition !== 'object') { query.where('id', idOrCondition); } else if (idOrCondition) { query.where(idOrCondition); } if (xcCondition) { - query.condition(xcCondition) + query.condition(xcCondition); } // console.log(query.toQuery()) - return query; } - public async metaDeleteAll(_project_id: string, _dbAlias: string): Promise { + public async metaDeleteAll( + _project_id: string, + _dbAlias: string + ): Promise { // await this.knexConnection..dropTableIfExists('nc_roles').; // await this.knexConnection.schema.dropTableIfExists('nc_store').; // await this.knexConnection.schema.dropTableIfExists('nc_hooks').; @@ -246,10 +275,13 @@ export default class NcMetaIOImpl extends NcMetaIO { // await this.knexConnection.schema.dropTableIfExists('nc_acl').; } - public async isMetaDataExists(project_id: string, dbAlias: string): Promise { + public async isMetaDataExists( + project_id: string, + dbAlias: string + ): Promise { const query = this.knexConnection('nc_models'); if (project_id !== null) { - query.where('project_id', project_id) + query.where('project_id', project_id); } if (dbAlias !== null) { query.where('db_alias', dbAlias); @@ -279,31 +311,43 @@ export default class NcMetaIOImpl extends NcMetaIO { } } - async metaReset(project_id: string, dbAlias: string, apiType?: string): Promise { + async metaReset( + project_id: string, + dbAlias: string, + apiType?: string + ): Promise { // const apiType: string = this.config?.envs?.[this.config.env || this.config.workingEnv]?.db.find(d => { // return d.meta.dbAlias === dbAlias; // })?.meta?.api?.type; if (apiType) { - await Promise.all(META_TABLES?.[apiType]?.map(table => { - return (async () => { - try { - await this.knexConnection(table).where({db_alias: dbAlias, project_id}).del(); - } catch (e) { - console.warn(`Error: ${table} reset failed`) - } - })() - })) - } - } - - public async projectCreate(projectName: string, config: any, description?: string, - meta?: boolean): Promise { + await Promise.all( + META_TABLES?.[apiType]?.map(table => { + return (async () => { + try { + await this.knexConnection(table) + .where({ db_alias: dbAlias, project_id }) + .del(); + } catch (e) { + console.warn(`Error: ${table} reset failed`); + } + })(); + }) + ); + } + } + + public async projectCreate( + projectName: string, + config: any, + description?: string, + meta?: boolean + ): Promise { try { const ranId = this.getNanoId(); const id = `${projectName.toLowerCase().replace(/\W+/g, '_')}_${ranId}`; if (meta) { - config.prefix = `nc_${ranId}__` + config.prefix = `nc_${ranId}__`; // if(config.envs._noco?.db?.[0]?.meta?.tn){ // config.envs._noco.db[0].meta.tn += `_${prefix}` // } @@ -313,124 +357,180 @@ export default class NcMetaIOImpl extends NcMetaIO { id, title: projectName, description, - config: CryptoJS.AES.encrypt(JSON.stringify(config), this.config?.auth?.jwt?.secret).toString() + config: CryptoJS.AES.encrypt( + JSON.stringify(config), + this.config?.auth?.jwt?.secret + ).toString() }; // todo: check project name used or not await this.knexConnection('nc_projects').insert({ ...project, created_at: this.knexConnection?.fn?.now(), - updated_at: this.knexConnection?.fn?.now(), + updated_at: this.knexConnection?.fn?.now() }); return project; } catch (e) { - console.log(e) + console.log(e); } } - public async projectUpdate(projectId: string, - config: any): Promise { + public async projectUpdate(projectId: string, config: any): Promise { try { const project = { - config: CryptoJS.AES.encrypt(JSON.stringify(config, null, 2), this.config?.auth?.jwt?.secret).toString() + config: CryptoJS.AES.encrypt( + JSON.stringify(config, null, 2), + this.config?.auth?.jwt?.secret + ).toString() }; // todo: check project name used or not - await this.knexConnection('nc_projects').update(project).where({ - id: projectId - }); + await this.knexConnection('nc_projects') + .update(project) + .where({ + id: projectId + }); } catch (e) { - console.log(e) + console.log(e); } } public async projectList(): Promise { return (await this.knexConnection('nc_projects').select()).map(p => { - p.config = CryptoJS.AES.decrypt(p.config, this.config?.auth?.jwt?.secret).toString(CryptoJS.enc.Utf8) + p.config = CryptoJS.AES.decrypt( + p.config, + this.config?.auth?.jwt?.secret + ).toString(CryptoJS.enc.Utf8); return p; }); } public async userProjectList(userId: any): Promise { - - - return (await this.knexConnection('nc_projects') - .leftJoin(this.knexConnection('nc_projects_users') - .where(`nc_projects_users.user_id`, userId).as('user'), 'user.project_id', 'nc_projects.id') - .select('nc_projects.*') - .select('user.user_id') - //(SELECT `xc_users`.`email` - // FROM `xc_users` - // INNER JOIN `nc_projects_users` - // ON `nc_projects_users`.`user_id` = - // `xc_users`.`id` and `nc_projects_users`.project_id=`nc_projects`.id where `nc_projects_users`.`roles` like '%owner%' limit 1) - .select(this.knexConnection('xc_users') - .select('xc_users.email') - .innerJoin('nc_projects_users', 'nc_projects_users.user_id', '=', 'xc_users.id') - .where('nc_projects_users.roles', 'like', '%owner%') - .first() - .as('owner') - ) - ).map(p => { - p.allowed = p.user_id === userId; - p.config = CryptoJS.AES.decrypt(p.config, this.config?.auth?.jwt?.secret).toString(CryptoJS.enc.Utf8) - return p; - }); - } - - public async isUserHaveAccessToProject(projectId: string, userId: any): Promise { - return !!(await this.knexConnection('nc_projects_users').where({ - project_id: projectId, - user_id: userId - }).first()); - } - - public async projectGet(projectName: string, encrypt ?): Promise { - const project = await this.knexConnection('nc_projects').where({ - title: projectName - }).first(); + return ( + ( + await this.knexConnection('nc_projects') + .leftJoin( + this.knexConnection('nc_projects_users') + .where(`nc_projects_users.user_id`, userId) + .as('user'), + 'user.project_id', + 'nc_projects.id' + ) + .select('nc_projects.*') + .select('user.user_id') + //(SELECT `xc_users`.`email` + // FROM `xc_users` + // INNER JOIN `nc_projects_users` + // ON `nc_projects_users`.`user_id` = + // `xc_users`.`id` and `nc_projects_users`.project_id=`nc_projects`.id where `nc_projects_users`.`roles` like '%owner%' limit 1) + .select( + this.knexConnection('xc_users') + .select('xc_users.email') + .innerJoin( + 'nc_projects_users', + 'nc_projects_users.user_id', + '=', + 'xc_users.id' + ) + .where('nc_projects_users.roles', 'like', '%owner%') + .first() + .as('owner') + ) + ).map(p => { + p.allowed = p.user_id === userId; + p.config = CryptoJS.AES.decrypt( + p.config, + this.config?.auth?.jwt?.secret + ).toString(CryptoJS.enc.Utf8); + return p; + }) + ); + } + + public async isUserHaveAccessToProject( + projectId: string, + userId: any + ): Promise { + return !!(await this.knexConnection('nc_projects_users') + .where({ + project_id: projectId, + user_id: userId + }) + .first()); + } + + public async projectGet(projectName: string, encrypt?): Promise { + const project = await this.knexConnection('nc_projects') + .where({ + title: projectName + }) + .first(); if (project && !encrypt) { - project.config = CryptoJS.AES.decrypt(project.config, this.config?.auth?.jwt?.secret).toString(CryptoJS.enc.Utf8) + project.config = CryptoJS.AES.decrypt( + project.config, + this.config?.auth?.jwt?.secret + ).toString(CryptoJS.enc.Utf8); } return project; } - public async projectGetById(projectId: string, encrypt ?): Promise { - const project = await this.knexConnection('nc_projects').where({ - id: projectId - }).first(); + public async projectGetById(projectId: string, encrypt?): Promise { + const project = await this.knexConnection('nc_projects') + .where({ + id: projectId + }) + .first(); if (project && !encrypt) { - project.config = CryptoJS.AES.decrypt(project.config, this.config?.auth?.jwt?.secret).toString(CryptoJS.enc.Utf8) + project.config = CryptoJS.AES.decrypt( + project.config, + this.config?.auth?.jwt?.secret + ).toString(CryptoJS.enc.Utf8); } return project; } - public projectDelete(title: string): Promise { - return this.knexConnection('nc_projects').where({ - title - }).delete(); + return this.knexConnection('nc_projects') + .where({ + title + }) + .delete(); } public projectDeleteById(id: string): Promise { - return this.knexConnection('nc_projects').where({ - id - }).delete(); - } - - public async projectStatusUpdate(projectName: string, status: string): Promise { - return this.knexConnection('nc_projects').update({ - status - }).where({ - title: projectName - }); + return this.knexConnection('nc_projects') + .where({ + id + }) + .delete(); + } + + public async projectStatusUpdate( + projectName: string, + status: string + ): Promise { + return this.knexConnection('nc_projects') + .update({ + status + }) + .where({ + title: projectName + }); } - public async projectAddUser(projectId: string, userId: any, roles: string): Promise { - if (await this.knexConnection('nc_projects_users').where({ - user_id: userId, - project_id: projectId - }).first()) { - return {} + public async projectAddUser( + projectId: string, + userId: any, + roles: string + ): Promise { + if ( + await this.knexConnection('nc_projects_users') + .where({ + user_id: userId, + project_id: projectId + }) + .first() + ) { + return {}; } return this.knexConnection('nc_projects_users').insert({ user_id: userId, @@ -440,23 +540,30 @@ export default class NcMetaIOImpl extends NcMetaIO { } public projectRemoveUser(projectId: string, userId: any): Promise { - return this.knexConnection('nc_projects_users').where({ - user_id: userId, - project_id: projectId - }).delete(); + return this.knexConnection('nc_projects_users') + .where({ + user_id: userId, + project_id: projectId + }) + .delete(); } - get isRest(): boolean { - return this.config?.envs?.[this.config.workingEnv]?.db?.some(db => db?.meta?.api?.type === 'rest'); + return this.config?.envs?.[this.config.workingEnv]?.db?.some( + db => db?.meta?.api?.type === 'rest' + ); } get isGql(): boolean { - return this.config?.envs?.[this.config.workingEnv]?.db?.some(db => db?.meta?.api?.type === 'graphql'); + return this.config?.envs?.[this.config.workingEnv]?.db?.some( + db => db?.meta?.api?.type === 'graphql' + ); } get isGrpc(): boolean { - return this.config?.envs?.[this.config.workingEnv]?.db?.some(db => db?.meta?.api?.type === 'grpc'); + return this.config?.envs?.[this.config.workingEnv]?.db?.some( + db => db?.meta?.api?.type === 'grpc' + ); } public get knex(): any { @@ -464,17 +571,22 @@ export default class NcMetaIOImpl extends NcMetaIO { } private getNanoId() { - return nanoid() + return nanoid(); } - public async audit(project_id: string, dbAlias: string, target: string, data: any): Promise { + public async audit( + project_id: string, + dbAlias: string, + target: string, + data: any + ): Promise { if (['DATA', 'COMMENT'].includes(data?.op_type)) { return Promise.resolve(undefined); } - return this.metaInsert(project_id, dbAlias, target, data) + return this.metaInsert(project_id, dbAlias, target, data); } - -}/** +} +/** * @copyright Copyright (c) 2021, Xgene Cloud Ltd * * @author Naveen MR diff --git a/packages/nocodb/src/lib/noco/meta/NcMetaIOImplEE.ts b/packages/nocodb/src/lib/noco/meta/NcMetaIOImplEE.ts index 462cc34e18..b92cfe6cb9 100644 --- a/packages/nocodb/src/lib/noco/meta/NcMetaIOImplEE.ts +++ b/packages/nocodb/src/lib/noco/meta/NcMetaIOImplEE.ts @@ -1,10 +1,16 @@ -import NcMetaIOImpl from "./NcMetaIOImpl"; +import NcMetaIOImpl from './NcMetaIOImpl'; export default class NcMetaIOImplEE extends NcMetaIOImpl { - public async audit(project_id: string, dbAlias: string, target: string, data: any): Promise { + public async audit( + project_id: string, + dbAlias: string, + target: string, + data: any + ): Promise { return this.metaInsert(project_id, dbAlias, target, data); } -}/** +} +/** * @copyright Copyright (c) 2021, Xgene Cloud Ltd * * @author Naveen MR diff --git a/packages/nocodb/src/lib/noco/migrations/nc_001_init.ts b/packages/nocodb/src/lib/noco/migrations/nc_001_init.ts index 44ddda8763..96de9bb02e 100644 --- a/packages/nocodb/src/lib/noco/migrations/nc_001_init.ts +++ b/packages/nocodb/src/lib/noco/migrations/nc_001_init.ts @@ -1,9 +1,9 @@ // import s3 from "../plugins/s3"; // import gcs from "../plugins/gcs"; // import smtp from "../plugins/smtp"; -import cache from "../plugins/cache"; -import googleAuth from "../plugins/googleAuth"; -import ses from "../plugins/ses"; +import cache from '../plugins/cache'; +import googleAuth from '../plugins/googleAuth'; +import ses from '../plugins/ses'; // import azure from "../plugins/azure"; // import brand from "../plugins/brand"; // import discord from "../plugins/discord"; @@ -14,7 +14,7 @@ import ses from "../plugins/ses"; // import spaces from "../plugins/spaces"; // import githubAuth from "../instant/common/plugins/githubAuth"; -const up = async (knex) => { +const up = async knex => { await knex.schema.createTable('nc_projects', table => { table.string('id', 128).primary(); table.string('title'); @@ -23,8 +23,7 @@ const up = async (knex) => { table.text('config'); table.text('meta'); table.timestamps(); - }) - + }); await knex.schema.createTable('nc_roles', table => { table.increments(); @@ -41,7 +40,8 @@ const up = async (knex) => { db_alias: '', project_id: '', title: 'owner', - description: 'Can add/remove creators. And full edit database structures & fields.', + description: + 'Can add/remove creators. And full edit database structures & fields.', type: 'SYSTEM' }, { @@ -55,7 +55,8 @@ const up = async (knex) => { db_alias: '', project_id: '', title: 'editor', - description: 'Can edit records but cannot change structure of database/fields', + description: + 'Can edit records but cannot change structure of database/fields', type: 'SYSTEM' }, { @@ -71,7 +72,7 @@ const up = async (knex) => { title: 'viewer', description: 'Can view the records but cannot edit anything', type: 'SYSTEM' - }, + } // { // db_alias: '', // project_id: '', @@ -79,12 +80,12 @@ const up = async (knex) => { // description: 'API access for an unauthorized user', // type: 'SYSTEM' // }, - ]) + ]); await knex.schema.createTable('nc_hooks', table => { table.increments(); table.string('project_id'); - table.string('db_alias').defaultTo('db') + table.string('db_alias').defaultTo('db'); table.string('title'); table.string('description', 255); table.string('env').defaultTo('all'); @@ -107,28 +108,25 @@ const up = async (knex) => { table.integer('timeout').defaultTo(60000); table.boolean('active').defaultTo(true); - table.timestamps(); - }) + }); await knex('nc_hooks').insert({ // url: 'http://localhost:4000/auth/hook', type: 'AUTH_MIDDLEWARE' - }) - + }); await knex.schema.createTable('nc_store', table => { table.increments(); table.string('project_id'); - table.string('db_alias').defaultTo('db') + table.string('db_alias').defaultTo('db'); table.string('key').index(); table.text('value', 'text'); table.string('type'); table.string('env'); table.string('tag'); table.timestamps(); - }) - + }); await knex('nc_store').insert({ key: 'NC_DEBUG', @@ -139,22 +137,21 @@ const up = async (knex) => { 'nc:api:gql': false, 'nc:api:grpc': false, 'nc:migrator': false, - 'nc:datamapper': false, + 'nc:datamapper': false }), db_alias: '' - }) + }); await knex('nc_store').insert({ key: 'NC_PROJECT_COUNT', value: '0', db_alias: '' - }) - + }); await knex.schema.createTable('nc_cron', table => { table.increments(); table.string('project_id'); - table.string('db_alias').defaultTo('db') + table.string('db_alias').defaultTo('db'); table.string('title'); table.string('description', 255); table.string('env'); @@ -170,30 +167,28 @@ const up = async (knex) => { table.integer('timeout').defaultTo(60000); table.timestamps(); - }) - + }); await knex.schema.createTable('nc_acl', table => { table.increments(); table.string('project_id'); - table.string('db_alias').defaultTo('db') + table.string('db_alias').defaultTo('db'); table.string('tn'); table.text('acl'); table.string('type').defaultTo('table'); table.timestamps(); - }) - + }); await knex.schema.createTable('nc_models', table => { table.increments(); table.string('project_id'); - table.string('db_alias').defaultTo('db') + table.string('db_alias').defaultTo('db'); table.string('title'); table.string('alias'); table.string('type').defaultTo('table'); table.text('meta', 'mediumtext'); table.text('schema', 'text'); - table.text('schema_previous', 'text') + table.text('schema_previous', 'text'); table.text('services', 'mediumtext'); table.text('messages', 'text'); table.boolean('enabled').defaultTo(true); @@ -207,14 +202,13 @@ const up = async (knex) => { table.boolean('pinned'); table.timestamps(); - table.index(['db_alias', 'title']) - }) - + table.index(['db_alias', 'title']); + }); await knex.schema.createTable('nc_relations', table => { table.increments(); table.string('project_id'); - table.string('db_alias') + table.string('db_alias'); table.string('tn'); table.string('rtn'); table.string('_tn'); @@ -230,13 +224,13 @@ const up = async (knex) => { table.string('dr'); table.timestamps(); - table.index(['db_alias', 'tn']) - }) + table.index(['db_alias', 'tn']); + }); await knex.schema.createTable('nc_routes', table => { table.increments(); table.string('project_id'); - table.string('db_alias').defaultTo('db') + table.string('db_alias').defaultTo('db'); table.string('title'); table.string('tn'); table.string('tnp'); @@ -252,13 +246,13 @@ const up = async (knex) => { table.boolean('is_custom'); // table.text('placeholder', 'longtext'); table.timestamps(); - table.index(['db_alias', 'title', 'tn']) - }) + table.index(['db_alias', 'title', 'tn']); + }); - await knex.schema.createTable('nc_resolvers', (table) => { + await knex.schema.createTable('nc_resolvers', table => { table.increments(); table.string('project_id'); - table.string('db_alias').defaultTo('db') + table.string('db_alias').defaultTo('db'); table.string('title'); table.text('resolver', 'text'); table.string('type'); @@ -267,12 +261,12 @@ const up = async (knex) => { table.integer('handler_type').defaultTo(1); // table.text('placeholder', 'text'); table.timestamps(); - }) + }); await knex.schema.createTable('nc_loaders', table => { table.increments(); table.string('project_id'); - table.string('db_alias').defaultTo('db') + table.string('db_alias').defaultTo('db'); table.string('title'); table.string('parent'); table.string('child'); @@ -280,12 +274,12 @@ const up = async (knex) => { table.string('resolver'); table.text('functions'); table.timestamps(); - }) + }); - await knex.schema.createTable('nc_rpc', (table) => { + await knex.schema.createTable('nc_rpc', table => { table.increments(); table.string('project_id'); - table.string('db_alias').defaultTo('db') + table.string('db_alias').defaultTo('db'); table.string('title'); table.string('tn'); table.text('service', 'text'); @@ -301,17 +295,20 @@ const up = async (knex) => { table.integer('handler_type').defaultTo(1); // table.text('placeholder', 'text'); table.timestamps(); - }) - await knex.schema.createTable('nc_projects_users', (table) => { - table.string('project_id').index() // .references('id').inTable('nc_projects') + }); + await knex.schema.createTable('nc_projects_users', table => { + table.string('project_id').index(); // .references('id').inTable('nc_projects') // todo: foreign key - table.integer('user_id').unsigned().index()//.references('id').inTable('xc_users') + table + .integer('user_id') + .unsigned() + .index(); //.references('id').inTable('xc_users') table.text('roles'); // table.text('placeholder', 'text'); table.timestamps(); - }) + }); - await knex.schema.createTable('nc_shared_views', (table) => { + await knex.schema.createTable('nc_shared_views', table => { table.increments(); table.string('project_id'); table.string('db_alias'); @@ -323,9 +320,9 @@ const up = async (knex) => { table.boolean('allow_copy'); table.string('password'); table.timestamps(); - }) + }); - await knex.schema.createTable('nc_disabled_models_for_role', (table) => { + await knex.schema.createTable('nc_disabled_models_for_role', table => { table.increments(); table.string('project_id'); table.string('db_alias', 45); @@ -340,10 +337,13 @@ const up = async (knex) => { table.string('rcn'); table.string('relation_type'); table.timestamps(); - table.index(['project_id', 'db_alias', 'title', 'type', 'role'], 'xc_disabled124_idx'); - }) + table.index( + ['project_id', 'db_alias', 'title', 'type', 'role'], + 'xc_disabled124_idx' + ); + }); - await knex.schema.createTable('nc_plugins', (table) => { + await knex.schema.createTable('nc_plugins', table => { table.increments(); table.string('project_id'); table.string('db_alias'); @@ -365,18 +365,17 @@ const up = async (knex) => { table.string('creator_website'); table.string('price'); table.timestamps(); - }) + }); await knex('nc_plugins').insert([ googleAuth, ses, - cache, + cache // ee, // brand, - ]) - + ]); - await knex.schema.createTable('nc_audit', (table) => { + await knex.schema.createTable('nc_audit', table => { table.increments(); table.string('user'); table.string('ip'); @@ -390,31 +389,32 @@ const up = async (knex) => { table.string('status'); table.text('description'); table.text('details'); - table.index(['db_alias', 'project_id', 'model_name', 'model_id'], '`nc_audit_index`') + table.index( + ['db_alias', 'project_id', 'model_name', 'model_id'], + '`nc_audit_index`' + ); table.timestamps(); - }) - + }); - await knex.schema.createTable('nc_migrations', (table) => { + await knex.schema.createTable('nc_migrations', table => { table.increments(); table.string('project_id'); table.string('db_alias'); table.text('up'); table.text('down'); - - table.string("title").notNullable(); - table.string("title_down").nullable(); - table.string("description").nullable(); - table.integer("batch").nullable(); - table.string("checksum").nullable(); - table.integer("status").nullable(); + table.string('title').notNullable(); + table.string('title_down').nullable(); + table.string('description').nullable(); + table.integer('batch').nullable(); + table.string('checksum').nullable(); + table.integer('status').nullable(); table.timestamps(); - }) + }); - await knex.schema.createTable('nc_api_tokens', (table) => { + await knex.schema.createTable('nc_api_tokens', table => { table.increments(); table.string('project_id'); table.string('db_alias'); @@ -424,12 +424,10 @@ const up = async (knex) => { table.string('expiry'); table.boolean('enabled').defaultTo(true); table.timestamps(); - }) - - + }); }; -const down = async (knex) => { +const down = async knex => { await knex.schema.dropTable('nc_plugins'); await knex.schema.dropTable('nc_disabled_models_for_role'); await knex.schema.dropTable('nc_shared_views'); @@ -451,7 +449,4 @@ const down = async (knex) => { await knex.schema.dropTable('nc_api_tokens'); }; - -export { - up, down -} +export { up, down }; diff --git a/packages/nocodb/src/lib/noco/migrations/nc_002_add_m2m.ts b/packages/nocodb/src/lib/noco/migrations/nc_002_add_m2m.ts index 60481ad1a5..3d375df166 100644 --- a/packages/nocodb/src/lib/noco/migrations/nc_002_add_m2m.ts +++ b/packages/nocodb/src/lib/noco/migrations/nc_002_add_m2m.ts @@ -1,43 +1,39 @@ -import Knex from "knex"; +import Knex from 'knex'; const up = async (knex: Knex) => { await knex.schema.alterTable('nc_models', table => { table.integer('mm'); table.text('m_to_m_meta'); - }) + }); }; -const down = async (knex) => { +const down = async knex => { await knex.schema.alterTable('nc_models', table => { table.dropColumns('mm', 'm_to_m_meta'); - }) + }); }; +export { up, down }; -export { - up, down -} - - - /** - * @copyright Copyright (c) 2021, Xgene Cloud Ltd - * - * @author Naveen MR - * @author Pranav C Balan - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ \ No newline at end of file +/** + * @copyright Copyright (c) 2021, Xgene Cloud Ltd + * + * @author Naveen MR + * @author Pranav C Balan + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ diff --git a/packages/nocodb/src/lib/noco/migrations/nc_003_add_fkn_column.ts b/packages/nocodb/src/lib/noco/migrations/nc_003_add_fkn_column.ts index ed034e70a4..d4f6f72a6f 100644 --- a/packages/nocodb/src/lib/noco/migrations/nc_003_add_fkn_column.ts +++ b/packages/nocodb/src/lib/noco/migrations/nc_003_add_fkn_column.ts @@ -1,22 +1,18 @@ -import Knex from "knex"; +import Knex from 'knex'; const up = async (knex: Knex) => { await knex.schema.alterTable('nc_relations', table => { table.string('fkn'); - }) + }); }; -const down = async (knex) => { +const down = async knex => { await knex.schema.alterTable('nc_relations', table => { table.dropColumns('fkn'); - }) + }); }; - -export { - up, down -} - +export { up, down }; /** * @copyright Copyright (c) 2021, Xgene Cloud Ltd diff --git a/packages/nocodb/src/lib/noco/nc.try.ts b/packages/nocodb/src/lib/noco/nc.try.ts index a8d600ee4a..1d0271d655 100644 --- a/packages/nocodb/src/lib/noco/nc.try.ts +++ b/packages/nocodb/src/lib/noco/nc.try.ts @@ -1,13 +1,11 @@ - import cors from 'cors'; import express from 'express'; -import NcConfigFactory from "../utils/NcConfigFactory"; - -import Noco from "./Noco"; +import NcConfigFactory from '../utils/NcConfigFactory'; +import Noco from './Noco'; -export default async function (dbUrl): Promise { +export default async function(dbUrl): Promise { const server = express(); server.use(cors()); @@ -15,22 +13,31 @@ export default async function (dbUrl): Promise { process.env[`NC_TRY`] = 'true'; - (async () => { const app = new Noco(); - server.use(await app.init({ - async afterMetaMigrationInit(): Promise { - - const config = NcConfigFactory.makeProjectConfigFromUrl(dbUrl); - await app.ncMeta.projectCreate('Dvdrental (Sample SQLite Database)', config, ''); - await app.ncMeta.projectStatusUpdate('Dvdrental (Sample SQLite Database)', 'started'); - - } - })); + server.use( + await app.init({ + async afterMetaMigrationInit(): Promise { + const config = NcConfigFactory.makeProjectConfigFromUrl(dbUrl); + await app.ncMeta.projectCreate( + 'Dvdrental (Sample SQLite Database)', + config, + '' + ); + await app.ncMeta.projectStatusUpdate( + 'Dvdrental (Sample SQLite Database)', + 'started' + ); + } + }) + ); server.listen(process.env.PORT || 8080, () => { - console.log(`App started successfully.\nVisit -> http://localhost:${process.env.PORT || 8080}/xc`); - }) - })().catch(e => console.log(e)) + console.log( + `App started successfully.\nVisit -> http://localhost:${process.env + .PORT || 8080}/xc` + ); + }); + })().catch(e => console.log(e)); } /** diff --git a/packages/nocodb/src/lib/noco/plugins/NcPluginMgr.ts b/packages/nocodb/src/lib/noco/plugins/NcPluginMgr.ts index 88d1f3e759..5e43a407a8 100644 --- a/packages/nocodb/src/lib/noco/plugins/NcPluginMgr.ts +++ b/packages/nocodb/src/lib/noco/plugins/NcPluginMgr.ts @@ -4,31 +4,32 @@ import { IWebhookNotificationAdapter, XcEmailPlugin, XcPlugin, - XcStoragePlugin, XcWebhookNotificationPlugin -} from "nc-plugin"; - -import BackblazePluginConfig from "../../../plugins/backblaze"; -import DiscordPluginConfig from "../../../plugins/discord"; -import GcsPluginConfig from "../../../plugins/gcs"; -import LinodePluginConfig from "../../../plugins/linode"; -import MattermostPluginConfig from "../../../plugins/mattermost"; -import MinioPluginConfig from "../../../plugins/mino"; -import OvhCloudPluginConfig from "../../../plugins/ovhCloud"; -import S3PluginConfig from "../../../plugins/s3"; -import ScalewayPluginConfig from "../../../plugins/scaleway"; -import SlackPluginConfig from "../../../plugins/slack"; -import SMTPPluginConfig from "../../../plugins/smtp"; -import MailerSendConfig from "../../../plugins/mailerSend"; -import SpacesPluginConfig from "../../../plugins/spaces"; -import TeamsPluginConfig from "../../../plugins/teams"; -import TwilioPluginConfig from "../../../plugins/twilio"; -import TwilioWhatsappPluginConfig from "../../../plugins/twilioWhatsapp"; -import UpcloudPluginConfig from "../../../plugins/upcloud"; -import VultrPluginConfig from "../../../plugins/vultr"; -import Noco from "../Noco"; -import NcMetaIO from "../meta/NcMetaIO"; - -import Local from "./adapters/storage/Local"; + XcStoragePlugin, + XcWebhookNotificationPlugin +} from 'nc-plugin'; + +import BackblazePluginConfig from '../../../plugins/backblaze'; +import DiscordPluginConfig from '../../../plugins/discord'; +import GcsPluginConfig from '../../../plugins/gcs'; +import LinodePluginConfig from '../../../plugins/linode'; +import MattermostPluginConfig from '../../../plugins/mattermost'; +import MinioPluginConfig from '../../../plugins/mino'; +import OvhCloudPluginConfig from '../../../plugins/ovhCloud'; +import S3PluginConfig from '../../../plugins/s3'; +import ScalewayPluginConfig from '../../../plugins/scaleway'; +import SlackPluginConfig from '../../../plugins/slack'; +import SMTPPluginConfig from '../../../plugins/smtp'; +import MailerSendConfig from '../../../plugins/mailerSend'; +import SpacesPluginConfig from '../../../plugins/spaces'; +import TeamsPluginConfig from '../../../plugins/teams'; +import TwilioPluginConfig from '../../../plugins/twilio'; +import TwilioWhatsappPluginConfig from '../../../plugins/twilioWhatsapp'; +import UpcloudPluginConfig from '../../../plugins/upcloud'; +import VultrPluginConfig from '../../../plugins/vultr'; +import Noco from '../Noco'; +import NcMetaIO from '../meta/NcMetaIO'; + +import Local from './adapters/storage/Local'; const defaultPlugins = [ SlackPluginConfig, @@ -48,16 +49,15 @@ const defaultPlugins = [ UpcloudPluginConfig, SMTPPluginConfig, MailerSendConfig, - ScalewayPluginConfig, -] + ScalewayPluginConfig +]; class NcPluginMgr { - private ncMeta: NcMetaIO; private app: Noco; /* active plugins */ - private activePlugins: Array + private activePlugins: Array; constructor(app: Noco, ncMeta: NcMetaIO) { this.app = app; @@ -66,16 +66,13 @@ class NcPluginMgr { } public async init(): Promise { - /* Populate rows into nc_plugins table if not present */ for (const plugin of defaultPlugins) { - - const pluginConfig = (await this.ncMeta.metaGet(null, null, 'nc_plugins', { + const pluginConfig = await this.ncMeta.metaGet(null, null, 'nc_plugins', { title: plugin.title - })); + }); if (!pluginConfig) { - await this.ncMeta.metaInsert(null, null, 'nc_plugins', { title: plugin.title, version: plugin.version, @@ -85,12 +82,10 @@ class NcPluginMgr { category: plugin.category, input_schema: JSON.stringify(plugin.inputs) }); - } /* init only the active plugins */ if (pluginConfig?.active) { - const tempPlugin = new plugin.builder(this.app, plugin); this.activePlugins.push(tempPlugin); @@ -102,52 +97,62 @@ class NcPluginMgr { try { await tempPlugin.init(pluginConfig?.input); } catch (e) { - console.log(`Plugin(${plugin?.title}) initialization failed : ${e.message}`) + console.log( + `Plugin(${plugin?.title}) initialization failed : ${e.message}` + ); } } - } } - public async reInit(): Promise { this.activePlugins = []; await this.init(); } - public get storageAdapter(): IStorageAdapter { - return (this.activePlugins?.find(plugin => plugin instanceof XcStoragePlugin) as XcStoragePlugin)?.getAdapter() || new Local(); + return ( + (this.activePlugins?.find( + plugin => plugin instanceof XcStoragePlugin + ) as XcStoragePlugin)?.getAdapter() || new Local() + ); } public get emailAdapter(): IEmailAdapter { - return (this.activePlugins?.find(plugin => plugin instanceof XcEmailPlugin) as XcEmailPlugin)?.getAdapter(); + return (this.activePlugins?.find( + plugin => plugin instanceof XcEmailPlugin + ) as XcEmailPlugin)?.getAdapter(); } - public get webhookNotificationAdapters(): { [key: string]: IWebhookNotificationAdapter } { + public get webhookNotificationAdapters(): { + [key: string]: IWebhookNotificationAdapter; + } { return this.activePlugins?.reduce((obj, plugin) => { if (plugin instanceof XcWebhookNotificationPlugin) { - obj[plugin?.config?.title] = (plugin as XcWebhookNotificationPlugin)?.getAdapter() + obj[ + plugin?.config?.title + ] = (plugin as XcWebhookNotificationPlugin)?.getAdapter(); } return obj; }, {}); - } public async test(args: any): Promise { switch (args.category) { - case 'Storage': { - const plugin = defaultPlugins.find(pluginConfig => pluginConfig?.title === args.title); - const tempPlugin = new plugin.builder(this.app, plugin); - await tempPlugin.init(args?.input); - return tempPlugin?.getAdapter()?.test?.(); - } + case 'Storage': + { + const plugin = defaultPlugins.find( + pluginConfig => pluginConfig?.title === args.title + ); + const tempPlugin = new plugin.builder(this.app, plugin); + await tempPlugin.init(args?.input); + return tempPlugin?.getAdapter()?.test?.(); + } break; default: throw new Error('Not implemented'); } } - } export default NcPluginMgr; @@ -173,4 +178,4 @@ export default NcPluginMgr; * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . * - */ \ No newline at end of file + */ diff --git a/packages/nocodb/src/lib/noco/plugins/adapters/cache/XcCache.ts b/packages/nocodb/src/lib/noco/plugins/adapters/cache/XcCache.ts index cd855502c1..0a057ade34 100644 --- a/packages/nocodb/src/lib/noco/plugins/adapters/cache/XcCache.ts +++ b/packages/nocodb/src/lib/noco/plugins/adapters/cache/XcCache.ts @@ -1,9 +1,7 @@ import LRU from 'lru-cache'; export default class XcCache { - public static init(config: any, overwrite = false) { - if (overwrite && this.instance) { this.instance.reset(); this.instance = null; @@ -11,18 +9,18 @@ export default class XcCache { if (!this.instance) { const options = { - max: 500, maxAge: 1000 * 60 * 60 - } + max: 500, + maxAge: 1000 * 60 * 60 + }; if (config) { const input = JSON.parse(config.input); Object.assign(options, input); } - this.instance = new LRU(options) + this.instance = new LRU(options); } } - public static get(key): any { return this.instance?.get(key); } @@ -36,5 +34,4 @@ export default class XcCache { } private static instance: LRU; - -} \ No newline at end of file +} diff --git a/packages/nocodb/src/lib/noco/plugins/adapters/discord/Discord.ts b/packages/nocodb/src/lib/noco/plugins/adapters/discord/Discord.ts index e353fde584..921fe8f41b 100644 --- a/packages/nocodb/src/lib/noco/plugins/adapters/discord/Discord.ts +++ b/packages/nocodb/src/lib/noco/plugins/adapters/discord/Discord.ts @@ -1,19 +1,20 @@ import axios from 'axios'; export default class Discord { - - public static async sendMessage(content: string, webhooks: Array<{ - webhook_url: string - }>): Promise { - for (const {webhook_url} of webhooks) { + public static async sendMessage( + content: string, + webhooks: Array<{ + webhook_url: string; + }> + ): Promise { + for (const { webhook_url } of webhooks) { try { await axios.post(webhook_url, { content - }) - }catch(e){ - console.log(e) + }); + } catch (e) { + console.log(e); } } } - -} \ No newline at end of file +} diff --git a/packages/nocodb/src/lib/noco/plugins/adapters/email/EmailFactory.ts b/packages/nocodb/src/lib/noco/plugins/adapters/email/EmailFactory.ts index 3cdfe9f844..16589562c4 100644 --- a/packages/nocodb/src/lib/noco/plugins/adapters/email/EmailFactory.ts +++ b/packages/nocodb/src/lib/noco/plugins/adapters/email/EmailFactory.ts @@ -1,10 +1,9 @@ -import IEmailAdapter from "../../../../../interface/IEmailAdapter"; +import IEmailAdapter from '../../../../../interface/IEmailAdapter'; -import SES from "./SES"; -import SMTP from "./SMTP"; +import SES from './SES'; +import SMTP from './SMTP'; export default class EmailFactory { - private static instance: IEmailAdapter; // tslint:disable-next-line:typedef @@ -34,12 +33,8 @@ export default class EmailFactory { break; } } - - - } - /** * @copyright Copyright (c) 2021, Xgene Cloud Ltd * @@ -62,4 +57,3 @@ export default class EmailFactory { * along with this program. If not, see . * */ - diff --git a/packages/nocodb/src/lib/noco/plugins/adapters/email/SES.ts b/packages/nocodb/src/lib/noco/plugins/adapters/email/SES.ts index d39b4051e6..7d27f04306 100644 --- a/packages/nocodb/src/lib/noco/plugins/adapters/email/SES.ts +++ b/packages/nocodb/src/lib/noco/plugins/adapters/email/SES.ts @@ -1,30 +1,22 @@ // @ts-ignore -import IEmailAdapter, {XcEmail} from "../../../../../interface/IEmailAdapter"; +import IEmailAdapter, { XcEmail } from '../../../../../interface/IEmailAdapter'; export default // @ts-ignore class SES implements IEmailAdapter { - - // @ts-ignore private input: any; - constructor(input:any) { - this.input= input; - } - - public async init(): Promise { - + constructor(input: any) { + this.input = input; } - public async mailSend(_mail:XcEmail): Promise { + public async init(): Promise {} - } + public async mailSend(_mail: XcEmail): Promise {} test(_email): Promise { return Promise.resolve(false); } - - } /** diff --git a/packages/nocodb/src/lib/noco/plugins/adapters/email/SMTP.ts b/packages/nocodb/src/lib/noco/plugins/adapters/email/SMTP.ts index cb99dfbb4b..e8635cc705 100644 --- a/packages/nocodb/src/lib/noco/plugins/adapters/email/SMTP.ts +++ b/packages/nocodb/src/lib/noco/plugins/adapters/email/SMTP.ts @@ -1,12 +1,11 @@ // @ts-ignore import nodemailer from 'nodemailer'; -import Mail from "nodemailer/lib/mailer"; +import Mail from 'nodemailer/lib/mailer'; -import IEmailAdapter, {XcEmail} from "../../../../../interface/IEmailAdapter"; +import IEmailAdapter, { XcEmail } from '../../../../../interface/IEmailAdapter'; export default // @ts-ignore class SMTP implements IEmailAdapter { - private transporter: Mail; private input: any; @@ -18,22 +17,22 @@ class SMTP implements IEmailAdapter { const config = { // from: this.input.from, // options: { - "host": this.input?.host, - "port": parseInt(this.input?.port, 10), - "secure": this.input?.secure === 'true', - "ignoreTLS": this.input?.ignoreTLS === 'true', - "auth": { - "user": this.input?.username, - "pass": this.input?.password + host: this.input?.host, + port: parseInt(this.input?.port, 10), + secure: this.input?.secure === 'true', + ignoreTLS: this.input?.ignoreTLS === 'true', + auth: { + user: this.input?.username, + pass: this.input?.password } // } - } + }; this.transporter = nodemailer.createTransport(config); } public async mailSend(mail: XcEmail): Promise { if (this.transporter) { - await this.transporter.sendMail({...mail, from: this.input.from}) + await this.transporter.sendMail({ ...mail, from: this.input.from }); } } @@ -41,9 +40,9 @@ class SMTP implements IEmailAdapter { try { this.mailSend({ to: email, - subject: "Test email", + subject: 'Test email', html: 'Test email' - } as any) + } as any); return true; } catch (e) { throw e; diff --git a/packages/nocodb/src/lib/noco/plugins/adapters/mattermost/Mattermost.ts b/packages/nocodb/src/lib/noco/plugins/adapters/mattermost/Mattermost.ts index bdcdb058f5..797b88663c 100644 --- a/packages/nocodb/src/lib/noco/plugins/adapters/mattermost/Mattermost.ts +++ b/packages/nocodb/src/lib/noco/plugins/adapters/mattermost/Mattermost.ts @@ -1,19 +1,20 @@ import axios from 'axios'; export default class Mattermost { - - public static async sendMessage(text: string, webhooks: Array<{ - webhook_url: string - }>): Promise { - for (const {webhook_url} of webhooks) { + public static async sendMessage( + text: string, + webhooks: Array<{ + webhook_url: string; + }> + ): Promise { + for (const { webhook_url } of webhooks) { try { await axios.post(webhook_url, { text - }) - }catch(e){ - console.log(e) + }); + } catch (e) { + console.log(e); } } } - -} \ No newline at end of file +} diff --git a/packages/nocodb/src/lib/noco/plugins/adapters/slack/Slack.ts b/packages/nocodb/src/lib/noco/plugins/adapters/slack/Slack.ts index f0985784ee..8338fe18c9 100644 --- a/packages/nocodb/src/lib/noco/plugins/adapters/slack/Slack.ts +++ b/packages/nocodb/src/lib/noco/plugins/adapters/slack/Slack.ts @@ -1,19 +1,20 @@ import axios from 'axios'; export default class Slack { - - public static async sendMessage(text: string, webhooks: Array<{ - webhook_url: string - }>): Promise { - for (const {webhook_url} of webhooks) { + public static async sendMessage( + text: string, + webhooks: Array<{ + webhook_url: string; + }> + ): Promise { + for (const { webhook_url } of webhooks) { try { await axios.post(webhook_url, { text - }) + }); } catch (e) { - console.log(e) + console.log(e); } } } - -} \ No newline at end of file +} diff --git a/packages/nocodb/src/lib/noco/plugins/adapters/storage/Local.ts b/packages/nocodb/src/lib/noco/plugins/adapters/storage/Local.ts index df7270a042..2dbfc5f55e 100644 --- a/packages/nocodb/src/lib/noco/plugins/adapters/storage/Local.ts +++ b/packages/nocodb/src/lib/noco/plugins/adapters/storage/Local.ts @@ -1,15 +1,15 @@ -import fs from "fs"; -import path from "path"; +import fs from 'fs'; +import path from 'path'; -import mkdirp from "mkdirp"; +import mkdirp from 'mkdirp'; -import IStorageAdapter, {XcFile} from "../../../../../interface/IStorageAdapter"; -import NcConfigFactory from "../../../../utils/NcConfigFactory"; +import IStorageAdapter, { + XcFile +} from '../../../../../interface/IStorageAdapter'; +import NcConfigFactory from '../../../../utils/NcConfigFactory'; export default class Local implements IStorageAdapter { - - constructor() { - } + constructor() {} public async fileCreate(key: string, file: XcFile): Promise { const destPath = path.join(NcConfigFactory.getToolDir(), ...key.split('/')); @@ -28,7 +28,9 @@ export default class Local implements IStorageAdapter { public async fileRead(filePath: string): Promise { try { - const fileData = await fs.promises.readFile(path.join(NcConfigFactory.getToolDir(), ...filePath.split('/'))); + const fileData = await fs.promises.readFile( + path.join(NcConfigFactory.getToolDir(), ...filePath.split('/')) + ); return fileData; } catch (e) { throw e; @@ -42,8 +44,8 @@ export default class Local implements IStorageAdapter { test(): Promise { return Promise.resolve(false); } - -}/** +} +/** * @copyright Copyright (c) 2021, Xgene Cloud Ltd * * @author Naveen MR diff --git a/packages/nocodb/src/lib/noco/plugins/adapters/twilio/Twilio.ts b/packages/nocodb/src/lib/noco/plugins/adapters/twilio/Twilio.ts index d8877ece5d..bb64fb35d4 100644 --- a/packages/nocodb/src/lib/noco/plugins/adapters/twilio/Twilio.ts +++ b/packages/nocodb/src/lib/noco/plugins/adapters/twilio/Twilio.ts @@ -1,7 +1,6 @@ -import twilio from "twilio"; +import twilio from 'twilio'; export default class Twilio { - private static instance: Twilio; private input: any; @@ -17,7 +16,6 @@ export default class Twilio { // tslint:disable-next-line:typedef public static create(config: any, overwrite = false): Twilio { - if (this.instance && !overwrite) { return this.instance; } @@ -32,16 +30,14 @@ export default class Twilio { public async sendMessage(content: string, numbers: string[]): Promise { for (const num of numbers) { try { - await this.client.messages - .create({ - body: content, - from: this.input.from, - to: num - }) + await this.client.messages.create({ + body: content, + from: this.input.from, + to: num + }); } catch (e) { - console.log(e) + console.log(e); } } } - -} \ No newline at end of file +} diff --git a/packages/nocodb/src/lib/noco/plugins/azure.ts b/packages/nocodb/src/lib/noco/plugins/azure.ts index 83118e8bf0..cd6d9f1f38 100644 --- a/packages/nocodb/src/lib/noco/plugins/azure.ts +++ b/packages/nocodb/src/lib/noco/plugins/azure.ts @@ -1,52 +1,58 @@ -import {XcActionType, XcForm, XcType} from "nc-common"; - +import { XcActionType, XcForm, XcType } from 'nc-common'; const input: XcForm = { title: 'Configure Azure Storage', - items: [{ - key: 'account', - label: 'Azure Account Name', - placeholder: 'Azure Account Name', - type: XcType.SingleLineText, - required: true - }, { - key: 'container', - label: 'Storage Container', - placeholder: 'Storage Container', - type: XcType.SingleLineText, - required: true - }, { - key: 'access_key', - label: 'Access Key', - placeholder: 'Access Key', - type: XcType.Password, - required: true - }], - actions: [{ - label: 'Test', - placeholder: 'Test', - key: 'test', - actionType: XcActionType.TEST, - type: XcType.Button - }, { - label: 'Save', - placeholder: 'Save', - key: 'save', - actionType: XcActionType.SUBMIT, - type: XcType.Button - },], - msgOnInstall:'Successfully installed and attachment will be stored in Azure', - msgOnUninstall:'', + items: [ + { + key: 'account', + label: 'Azure Account Name', + placeholder: 'Azure Account Name', + type: XcType.SingleLineText, + required: true + }, + { + key: 'container', + label: 'Storage Container', + placeholder: 'Storage Container', + type: XcType.SingleLineText, + required: true + }, + { + key: 'access_key', + label: 'Access Key', + placeholder: 'Access Key', + type: XcType.Password, + required: true + } + ], + actions: [ + { + label: 'Test', + placeholder: 'Test', + key: 'test', + actionType: XcActionType.TEST, + type: XcType.Button + }, + { + label: 'Save', + placeholder: 'Save', + key: 'save', + actionType: XcActionType.SUBMIT, + type: XcType.Button + } + ], + msgOnInstall: 'Successfully installed and attachment will be stored in Azure', + msgOnUninstall: '' }; - export default { title: 'Azure', version: '0.0.1', logo: 'plugins/azure.png', - description: 'Azure Blob storage is Microsoft\'s object storage solution for the cloud. Blob storage is optimized for storing massive amounts of unstructured data, such as text or binary data.', + description: + "Azure Blob storage is Microsoft's object storage solution for the cloud. Blob storage is optimized for storing massive amounts of unstructured data, such as text or binary data.", price: 'Free', tags: 'Storage', category: 'Storage', input_schema: JSON.stringify(input) -}; \ No newline at end of file +}; diff --git a/packages/nocodb/src/lib/noco/plugins/brand.ts b/packages/nocodb/src/lib/noco/plugins/brand.ts index c769bcfed9..3e6b11fdea 100644 --- a/packages/nocodb/src/lib/noco/plugins/brand.ts +++ b/packages/nocodb/src/lib/noco/plugins/brand.ts @@ -1,4 +1,4 @@ -import {XcActionType, XcForm, XcType} from "nc-common"; +import { XcActionType, XcForm, XcType } from 'nc-common'; const input: XcForm = { title: 'Branding', @@ -9,13 +9,15 @@ const input: XcForm = { placeholder: 'Title', type: XcType.SingleLineText, required: true - }, { + }, + { key: 'logo', label: 'Logo', placeholder: 'Logo', type: XcType.Attachment, required: true - }, { + }, + { key: 'favicon', label: 'Favicon', placeholder: 'Favicon', @@ -49,18 +51,21 @@ const input: XcForm = { placeholder: 'Youtube', type: XcType.URL, required: false - },], - actions: [{ - label: 'Save', - key: 'save', - actionType: XcActionType.SUBMIT, - type: XcType.Button - },], - msgOnInstall: 'Successfully installed and hard refresh the browser to reflect the changes', - msgOnUninstall: '', + } + ], + actions: [ + { + label: 'Save', + key: 'save', + actionType: XcActionType.SUBMIT, + type: XcType.Button + } + ], + msgOnInstall: + 'Successfully installed and hard refresh the browser to reflect the changes', + msgOnUninstall: '' }; - export default { title: 'Branding', version: '0.0.1', @@ -69,5 +74,5 @@ export default { price: 'Free', tags: 'Brand', category: 'Brand', - input_schema: JSON.stringify(input), -}; \ No newline at end of file + input_schema: JSON.stringify(input) +}; diff --git a/packages/nocodb/src/lib/noco/plugins/cache.ts b/packages/nocodb/src/lib/noco/plugins/cache.ts index 1cf12bc825..cde26aa785 100644 --- a/packages/nocodb/src/lib/noco/plugins/cache.ts +++ b/packages/nocodb/src/lib/noco/plugins/cache.ts @@ -1,39 +1,43 @@ -import {XcActionType, XcForm, XcType} from "nc-common"; - +import { XcActionType, XcForm, XcType } from 'nc-common'; const input: XcForm = { title: 'Configure Metadata LRU Cache', - items: [{ - key: 'max', - label: 'Maximum Size', - placeholder: 'Maximum Size', - type: XcType.SingleLineText, - required: true - }, { - key: 'maxAge', - label: 'Maximum Age(in ms)', - placeholder: 'Maximum Age(in ms)', - type: XcType.SingleLineText, - required: true - },], - actions: [{ - label: 'Test', - placeholder: 'Test', - key: 'test', - actionType: XcActionType.TEST, - type: XcType.Button - }, { - label: 'Save', - placeholder: 'Save', - key: 'save', - actionType: XcActionType.SUBMIT, - type: XcType.Button - },], + items: [ + { + key: 'max', + label: 'Maximum Size', + placeholder: 'Maximum Size', + type: XcType.SingleLineText, + required: true + }, + { + key: 'maxAge', + label: 'Maximum Age(in ms)', + placeholder: 'Maximum Age(in ms)', + type: XcType.SingleLineText, + required: true + } + ], + actions: [ + { + label: 'Test', + placeholder: 'Test', + key: 'test', + actionType: XcActionType.TEST, + type: XcType.Button + }, + { + label: 'Save', + placeholder: 'Save', + key: 'save', + actionType: XcActionType.SUBMIT, + type: XcType.Button + } + ], msgOnInstall: 'Successfully updated LRU cache options.', - msgOnUninstall: '', + msgOnUninstall: '' }; - export default { title: 'Metadata LRU Cache', version: '0.0.1', @@ -44,7 +48,8 @@ export default { category: 'Cache', active: true, input: JSON.stringify({ - max: 500, maxAge: 1000 * 60 * 60 * 24 + max: 500, + maxAge: 1000 * 60 * 60 * 24 }), input_schema: JSON.stringify(input) -}; \ No newline at end of file +}; diff --git a/packages/nocodb/src/lib/noco/plugins/discord.ts b/packages/nocodb/src/lib/noco/plugins/discord.ts index 73d413dd6f..450cab76c5 100644 --- a/packages/nocodb/src/lib/noco/plugins/discord.ts +++ b/packages/nocodb/src/lib/noco/plugins/discord.ts @@ -1,47 +1,53 @@ -import {XcActionType, XcForm, XcType} from "nc-common"; - +import { XcActionType, XcForm, XcType } from 'nc-common'; const input: XcForm = { title: 'Configure Discord', array: true, - items: [{ - key: 'channel', - label: 'Channel Name', - placeholder: 'Channel Name', - type: XcType.SingleLineText, - required: true - }, { - key: 'webhook_url', - label: 'Webhook URL', - type: XcType.Password, - placeholder: 'Webhook URL', - required: true - }], - actions: [{ - label: 'Test', - placeholder: 'Test', - key: 'test', - actionType: XcActionType.TEST, - type: XcType.Button - }, { - label: 'Save', - placeholder: 'Save', - key: 'save', - actionType: XcActionType.SUBMIT, - type: XcType.Button - },], - msgOnInstall:'Successfully installed and Discord is enabled for notification.', - msgOnUninstall:'', + items: [ + { + key: 'channel', + label: 'Channel Name', + placeholder: 'Channel Name', + type: XcType.SingleLineText, + required: true + }, + { + key: 'webhook_url', + label: 'Webhook URL', + type: XcType.Password, + placeholder: 'Webhook URL', + required: true + } + ], + actions: [ + { + label: 'Test', + placeholder: 'Test', + key: 'test', + actionType: XcActionType.TEST, + type: XcType.Button + }, + { + label: 'Save', + placeholder: 'Save', + key: 'save', + actionType: XcActionType.SUBMIT, + type: XcType.Button + } + ], + msgOnInstall: + 'Successfully installed and Discord is enabled for notification.', + msgOnUninstall: '' }; - export default { title: 'Discord', version: '0.0.1', logo: 'plugins/discord.png', - description: 'Discord is the easiest way to talk over voice, video, and text. Talk, chat, hang out, and stay close with your friends and communities.', + description: + 'Discord is the easiest way to talk over voice, video, and text. Talk, chat, hang out, and stay close with your friends and communities.', price: 'Free', tags: 'Chat', category: 'Chat', input_schema: JSON.stringify(input) -}; \ No newline at end of file +}; diff --git a/packages/nocodb/src/lib/noco/plugins/ee.ts b/packages/nocodb/src/lib/noco/plugins/ee.ts index 108132bb2b..3fd310f28d 100644 --- a/packages/nocodb/src/lib/noco/plugins/ee.ts +++ b/packages/nocodb/src/lib/noco/plugins/ee.ts @@ -1,41 +1,43 @@ -import {XcActionType, XcForm, XcType} from "nc-common"; - +import { XcActionType, XcForm, XcType } from 'nc-common'; const input: XcForm = { title: 'Configure Enterprise Edition', - items: [{ - key: 'key', - label: 'Key', - placeholder: 'Key', - type: XcType.Password, - required: true - }, + items: [ + { + key: 'key', + label: 'Key', + placeholder: 'Key', + type: XcType.Password, + required: true + } // { // key: 'callback_url', // label: 'Callback URL', // placeholder: 'Callback URL', // type: XcType.URL, // required: true - // }, + // }, + ], + actions: [ + { + label: 'Test', + placeholder: 'Test', + key: 'test', + actionType: XcActionType.TEST, + type: XcType.Button + }, + { + label: 'Save', + placeholder: 'Save', + key: 'save', + actionType: XcActionType.SUBMIT, + type: XcType.Button + } ], - actions: [{ - label: 'Test', - placeholder: 'Test', - key: 'test', - actionType: XcActionType.TEST, - type: XcType.Button - }, { - label: 'Save', - placeholder: 'Save', - key: 'save', - actionType: XcActionType.SUBMIT, - type: XcType.Button - },], msgOnInstall: 'Successfully installed and enabled Enterprise Edition.', - msgOnUninstall: '', + msgOnUninstall: '' }; - export default { title: 'Enterprise Edition', version: '0.0.1', @@ -45,4 +47,4 @@ export default { tags: 'Enterprise', category: 'Enterprise', input_schema: JSON.stringify(input) -}; \ No newline at end of file +}; diff --git a/packages/nocodb/src/lib/noco/plugins/githubAuth.ts b/packages/nocodb/src/lib/noco/plugins/githubAuth.ts index 8d0e9553a0..40332a6989 100644 --- a/packages/nocodb/src/lib/noco/plugins/githubAuth.ts +++ b/packages/nocodb/src/lib/noco/plugins/githubAuth.ts @@ -1,44 +1,51 @@ -import {XcActionType, XcForm, XcType} from "nc-common"; +import { XcActionType, XcForm, XcType } from 'nc-common'; const input: XcForm = { title: 'Configure Github Auth', - items: [{ - key: 'client_id', - label: 'Client ID', - placeholder: 'Client ID', - type: XcType.SingleLineText, - required: true - },{ - key: 'client_secret', - label: 'Client Secret', - placeholder: 'Client Secret', - type: XcType.Password, - required: true - },{ - key: 'redirect_url', - label: 'Redirect URL', - placeholder: 'Redirect URL', - type: XcType.SingleLineText, - required: true - }, ], - actions: [{ - label: 'Test', - placeholder: 'Test', - key: 'test', - actionType: XcActionType.TEST, - type: XcType.Button - }, { - label: 'Save', - placeholder: 'Save', - key: 'save', - actionType: XcActionType.SUBMIT, - type: XcType.Button - },], - msgOnInstall:'Successfully installed and configured Github Authentication, restart NocoDB', - msgOnUninstall:'', + items: [ + { + key: 'client_id', + label: 'Client ID', + placeholder: 'Client ID', + type: XcType.SingleLineText, + required: true + }, + { + key: 'client_secret', + label: 'Client Secret', + placeholder: 'Client Secret', + type: XcType.Password, + required: true + }, + { + key: 'redirect_url', + label: 'Redirect URL', + placeholder: 'Redirect URL', + type: XcType.SingleLineText, + required: true + } + ], + actions: [ + { + label: 'Test', + placeholder: 'Test', + key: 'test', + actionType: XcActionType.TEST, + type: XcType.Button + }, + { + label: 'Save', + placeholder: 'Save', + key: 'save', + actionType: XcActionType.SUBMIT, + type: XcType.Button + } + ], + msgOnInstall: + 'Successfully installed and configured Github Authentication, restart NocoDB', + msgOnUninstall: '' }; - export default { title: 'Github', version: '0.0.1', @@ -48,4 +55,4 @@ export default { tags: 'Authentication', category: 'Github', input_schema: JSON.stringify(input) -}; \ No newline at end of file +}; diff --git a/packages/nocodb/src/lib/noco/plugins/googleAuth.ts b/packages/nocodb/src/lib/noco/plugins/googleAuth.ts index de3043f07f..a35f5afbef 100644 --- a/packages/nocodb/src/lib/noco/plugins/googleAuth.ts +++ b/packages/nocodb/src/lib/noco/plugins/googleAuth.ts @@ -1,44 +1,51 @@ -import {XcActionType, XcForm, XcType} from "nc-common"; +import { XcActionType, XcForm, XcType } from 'nc-common'; const input: XcForm = { title: 'Configure Google Auth', - items: [{ - key: 'client_id', - label: 'Client ID', - placeholder: 'Client ID', - type: XcType.SingleLineText, - required: true - }, { - key: 'client_secret', - label: 'Client Secret', - placeholder: 'Client Secret', - type: XcType.Password, - required: true - }, { - key: 'redirect_url', - label: 'Redirect URL', - placeholder: 'Redirect URL', - type: XcType.SingleLineText, - required: true - },], - actions: [{ - label: 'Test', - placeholder: 'Test', - key: 'test', - actionType: XcActionType.TEST, - type: XcType.Button - }, { - label: 'Save', - placeholder: 'Save', - key: 'save', - actionType: XcActionType.SUBMIT, - type: XcType.Button - },], - msgOnInstall: 'Successfully installed and configured Google Authentication, restart NocoDB', - msgOnUninstall: '', + items: [ + { + key: 'client_id', + label: 'Client ID', + placeholder: 'Client ID', + type: XcType.SingleLineText, + required: true + }, + { + key: 'client_secret', + label: 'Client Secret', + placeholder: 'Client Secret', + type: XcType.Password, + required: true + }, + { + key: 'redirect_url', + label: 'Redirect URL', + placeholder: 'Redirect URL', + type: XcType.SingleLineText, + required: true + } + ], + actions: [ + { + label: 'Test', + placeholder: 'Test', + key: 'test', + actionType: XcActionType.TEST, + type: XcType.Button + }, + { + label: 'Save', + placeholder: 'Save', + key: 'save', + actionType: XcActionType.SUBMIT, + type: XcType.Button + } + ], + msgOnInstall: + 'Successfully installed and configured Google Authentication, restart NocoDB', + msgOnUninstall: '' }; - export default { title: 'Google', version: '0.0.1', @@ -48,4 +55,4 @@ export default { tags: 'Authentication', category: 'Google', input_schema: JSON.stringify(input) -}; \ No newline at end of file +}; diff --git a/packages/nocodb/src/lib/noco/plugins/mattermost.ts b/packages/nocodb/src/lib/noco/plugins/mattermost.ts index a64cc5b965..6a7e10bf5f 100644 --- a/packages/nocodb/src/lib/noco/plugins/mattermost.ts +++ b/packages/nocodb/src/lib/noco/plugins/mattermost.ts @@ -1,47 +1,53 @@ -import {XcActionType, XcForm, XcType} from "nc-common"; - +import { XcActionType, XcForm, XcType } from 'nc-common'; const input: XcForm = { title: 'Configure Mattermost', array: true, - items: [{ - key: 'channel', - label: 'Channel Name', - placeholder: 'Channel Name', - type: XcType.SingleLineText, - required: true - }, { - key: 'webhook_url', - label: 'Webhook URL', - placeholder: 'Webhook URL', - type: XcType.Password, - required: true - },], - actions: [{ - label: 'Test', - placeholder: 'Test', - key: 'test', - actionType: XcActionType.TEST, - type: XcType.Button - }, { - label: 'Save', - placeholder: 'Save', - key: 'save', - actionType: XcActionType.SUBMIT, - type: XcType.Button - },], - msgOnInstall: 'Successfully installed and Mattermost is enabled for notification.', - msgOnUninstall: '', + items: [ + { + key: 'channel', + label: 'Channel Name', + placeholder: 'Channel Name', + type: XcType.SingleLineText, + required: true + }, + { + key: 'webhook_url', + label: 'Webhook URL', + placeholder: 'Webhook URL', + type: XcType.Password, + required: true + } + ], + actions: [ + { + label: 'Test', + placeholder: 'Test', + key: 'test', + actionType: XcActionType.TEST, + type: XcType.Button + }, + { + label: 'Save', + placeholder: 'Save', + key: 'save', + actionType: XcActionType.SUBMIT, + type: XcType.Button + } + ], + msgOnInstall: + 'Successfully installed and Mattermost is enabled for notification.', + msgOnUninstall: '' }; - export default { title: 'Mattermost', version: '0.0.1', logo: 'plugins/mattermost.png', - description: 'Mattermost brings all your team communication into one place, making it searchable and accessible anywhere.', + description: + 'Mattermost brings all your team communication into one place, making it searchable and accessible anywhere.', price: 'Free', tags: 'Chat', category: 'Chat', input_schema: JSON.stringify(input) -}; \ No newline at end of file +}; diff --git a/packages/nocodb/src/lib/noco/plugins/ses.ts b/packages/nocodb/src/lib/noco/plugins/ses.ts index 340e9c9d4f..2e84964a83 100644 --- a/packages/nocodb/src/lib/noco/plugins/ses.ts +++ b/packages/nocodb/src/lib/noco/plugins/ses.ts @@ -1,69 +1,80 @@ -import {XcActionType, XcForm, XcType} from "nc-common"; +import { XcActionType, XcForm, XcType } from 'nc-common'; const input: XcForm = { title: 'Configure Amazon Simple Email Service(SES)', - items: [{ - key: 'from', - label: 'From', - placeholder: 'From', - type: XcType.SingleLineText, - required: true - }, { - key: 'host', - label: 'Jost', - placeholder: 'Jost', - type: XcType.SingleLineText, - required: true - }, { - key: 'port', - label: 'Port', - placeholder: 'Port', - type: XcType.SingleLineText, - required: true - }, { - key: 'secure', - label: 'Secure', - placeholder: 'Secure', - type: XcType.SingleLineText, - required: true - }, { - key: 'username', - label: 'Username', - placeholder: 'Username', - type: XcType.SingleLineText, - required: true - },{ - key: 'password', - label: 'Password', - placeholder: 'Password', - type: XcType.Password, - required: true - },], - actions: [{ - label: 'Test', - placeholder: 'Test', - key: 'test', - actionType: XcActionType.TEST, - type: XcType.Button - }, { - label: 'Save', - placeholder: 'Save', - key: 'save', - actionType: XcActionType.SUBMIT, - type: XcType.Button - },], - msgOnInstall:'Successfully installed and email notification will use Amazon SES', - msgOnUninstall:'', + items: [ + { + key: 'from', + label: 'From', + placeholder: 'From', + type: XcType.SingleLineText, + required: true + }, + { + key: 'host', + label: 'Jost', + placeholder: 'Jost', + type: XcType.SingleLineText, + required: true + }, + { + key: 'port', + label: 'Port', + placeholder: 'Port', + type: XcType.SingleLineText, + required: true + }, + { + key: 'secure', + label: 'Secure', + placeholder: 'Secure', + type: XcType.SingleLineText, + required: true + }, + { + key: 'username', + label: 'Username', + placeholder: 'Username', + type: XcType.SingleLineText, + required: true + }, + { + key: 'password', + label: 'Password', + placeholder: 'Password', + type: XcType.Password, + required: true + } + ], + actions: [ + { + label: 'Test', + placeholder: 'Test', + key: 'test', + actionType: XcActionType.TEST, + type: XcType.Button + }, + { + label: 'Save', + placeholder: 'Save', + key: 'save', + actionType: XcActionType.SUBMIT, + type: XcType.Button + } + ], + msgOnInstall: + 'Successfully installed and email notification will use Amazon SES', + msgOnUninstall: '' }; - export default { title: 'SES', version: '0.0.1', logo: 'plugins/aws.png', - description: 'Amazon Simple Email Service (SES) is a cost-effective, flexible, and scalable email service that enables developers to send mail from within any application.', + description: + 'Amazon Simple Email Service (SES) is a cost-effective, flexible, and scalable email service that enables developers to send mail from within any application.', price: 'Free', tags: 'Email', category: 'Email', input_schema: JSON.stringify(input) -}; \ No newline at end of file +}; diff --git a/packages/nocodb/src/lib/noco/plugins/slack.ts b/packages/nocodb/src/lib/noco/plugins/slack.ts index 78840b01b2..bc81a147cc 100644 --- a/packages/nocodb/src/lib/noco/plugins/slack.ts +++ b/packages/nocodb/src/lib/noco/plugins/slack.ts @@ -1,47 +1,52 @@ -import {XcActionType, XcForm, XcType} from "nc-common"; - +import { XcActionType, XcForm, XcType } from 'nc-common'; const input: XcForm = { title: 'Configure Slack', array: true, - items: [{ - key: 'channel', - label: 'Channel Name', - placeholder: 'Channel Name', - type: XcType.SingleLineText, - required: true - }, { - key: 'webhook_url', - label: 'Webhook URL', - placeholder: 'Webhook URL', - type: XcType.Password, - required: true - },], - actions: [{ - label: 'Test', - placeholder: 'Test', - key: 'test', - actionType: XcActionType.TEST, - type: XcType.Button - }, { - label: 'Save', - placeholder: 'Save', - key: 'save', - actionType: XcActionType.SUBMIT, - type: XcType.Button - },], + items: [ + { + key: 'channel', + label: 'Channel Name', + placeholder: 'Channel Name', + type: XcType.SingleLineText, + required: true + }, + { + key: 'webhook_url', + label: 'Webhook URL', + placeholder: 'Webhook URL', + type: XcType.Password, + required: true + } + ], + actions: [ + { + label: 'Test', + placeholder: 'Test', + key: 'test', + actionType: XcActionType.TEST, + type: XcType.Button + }, + { + label: 'Save', + placeholder: 'Save', + key: 'save', + actionType: XcActionType.SUBMIT, + type: XcType.Button + } + ], msgOnInstall: 'Successfully installed and Slack is enabled for notification.', - msgOnUninstall: '', + msgOnUninstall: '' }; - export default { title: 'Slack', version: '0.0.1', logo: 'plugins/slack.webp', - description: 'Slack brings team communication and collaboration into one place so you can get more work done, whether you belong to a large enterprise or a small business. ', + description: + 'Slack brings team communication and collaboration into one place so you can get more work done, whether you belong to a large enterprise or a small business. ', price: 'Free', tags: 'Chat', category: 'Chat', input_schema: JSON.stringify(input) -}; \ No newline at end of file +}; diff --git a/packages/nocodb/src/lib/noco/plugins/smtp.ts b/packages/nocodb/src/lib/noco/plugins/smtp.ts index a4c7b39729..01eafef5a4 100644 --- a/packages/nocodb/src/lib/noco/plugins/smtp.ts +++ b/packages/nocodb/src/lib/noco/plugins/smtp.ts @@ -1,66 +1,77 @@ -import {XcActionType, XcForm, XcType} from "nc-common"; +import { XcActionType, XcForm, XcType } from 'nc-common'; const input: XcForm = { title: 'Configure Email SMTP', - items: [{ - key: 'from', - label: 'From', - placeholder: 'eg: admin@example.com', - type: XcType.SingleLineText, - required: true - }, { - key: 'host', - label: 'Host', - placeholder: 'eg: smtp.example.com', - type: XcType.SingleLineText, - required: true - }, { - key: 'port', - label: 'Port', - placeholder: 'Port', - type: XcType.SingleLineText, - required: true - }, { - key: 'secure', - label: 'Secure', - placeholder: 'Secure', - type: XcType.SingleLineText, - required: true - }, { - key: 'ignoreTLS', - label: 'IgnoreTLS', - placeholder: 'IgnoreTLS', - type: XcType.SingleLineText, - required: true - }, { - key: 'username', - label: 'Username', - placeholder: 'Username', - type: XcType.SingleLineText, - required: true - }, { - key: 'password', - label: 'Password', - placeholder: 'Password', - type: XcType.Password, - required: true - },], - actions: [{ - label: 'Test', - key: 'test', - actionType: XcActionType.TEST, - type: XcType.Button - }, { - label: 'Save', - key: 'save', - actionType: XcActionType.SUBMIT, - type: XcType.Button - },], - msgOnInstall: 'Successfully installed and email notification will use SMTP configuration', - msgOnUninstall: '', + items: [ + { + key: 'from', + label: 'From', + placeholder: 'eg: admin@example.com', + type: XcType.SingleLineText, + required: true + }, + { + key: 'host', + label: 'Host', + placeholder: 'eg: smtp.example.com', + type: XcType.SingleLineText, + required: true + }, + { + key: 'port', + label: 'Port', + placeholder: 'Port', + type: XcType.SingleLineText, + required: true + }, + { + key: 'secure', + label: 'Secure', + placeholder: 'Secure', + type: XcType.SingleLineText, + required: true + }, + { + key: 'ignoreTLS', + label: 'IgnoreTLS', + placeholder: 'IgnoreTLS', + type: XcType.SingleLineText, + required: true + }, + { + key: 'username', + label: 'Username', + placeholder: 'Username', + type: XcType.SingleLineText, + required: true + }, + { + key: 'password', + label: 'Password', + placeholder: 'Password', + type: XcType.Password, + required: true + } + ], + actions: [ + { + label: 'Test', + key: 'test', + actionType: XcActionType.TEST, + type: XcType.Button + }, + { + label: 'Save', + key: 'save', + actionType: XcActionType.SUBMIT, + type: XcType.Button + } + ], + msgOnInstall: + 'Successfully installed and email notification will use SMTP configuration', + msgOnUninstall: '' }; - export default { title: 'SMTP', version: '0.0.1', @@ -70,4 +81,4 @@ export default { tags: 'Email', category: 'Email', input_schema: JSON.stringify(input) -}; \ No newline at end of file +}; diff --git a/packages/nocodb/src/lib/noco/plugins/twilio.ts b/packages/nocodb/src/lib/noco/plugins/twilio.ts index ad1d418261..d8c9dc1f77 100644 --- a/packages/nocodb/src/lib/noco/plugins/twilio.ts +++ b/packages/nocodb/src/lib/noco/plugins/twilio.ts @@ -1,52 +1,59 @@ -import {XcActionType, XcForm, XcType} from "nc-common"; - +import { XcActionType, XcForm, XcType } from 'nc-common'; const input: XcForm = { title: 'Configure Twilio', - items: [{ - key: 'sid', - label: 'Account SID', - placeholder: 'Account SID', - type: XcType.SingleLineText, - required: true - }, { - key: 'token', - label: 'Auth Token' , - placeholder: 'Auth Token', - type: XcType.Password, - required: true - }, { - key: 'from', - label: 'From Phone Number', - placeholder: 'From Phone Number', - type: XcType.SingleLineText, - required: true - },], - actions: [{ - label: 'Test', - placeholder: 'Test', - key: 'test', - actionType: XcActionType.TEST, - type: XcType.Button - }, { - label: 'Save', - placeholder: 'Save', - key: 'save', - actionType: XcActionType.SUBMIT, - type: XcType.Button - },], - msgOnInstall: 'Successfully installed and Twilio is enabled for notification.', - msgOnUninstall: '', + items: [ + { + key: 'sid', + label: 'Account SID', + placeholder: 'Account SID', + type: XcType.SingleLineText, + required: true + }, + { + key: 'token', + label: 'Auth Token', + placeholder: 'Auth Token', + type: XcType.Password, + required: true + }, + { + key: 'from', + label: 'From Phone Number', + placeholder: 'From Phone Number', + type: XcType.SingleLineText, + required: true + } + ], + actions: [ + { + label: 'Test', + placeholder: 'Test', + key: 'test', + actionType: XcActionType.TEST, + type: XcType.Button + }, + { + label: 'Save', + placeholder: 'Save', + key: 'save', + actionType: XcActionType.SUBMIT, + type: XcType.Button + } + ], + msgOnInstall: + 'Successfully installed and Twilio is enabled for notification.', + msgOnUninstall: '' }; - export default { title: 'Twilio', version: '0.0.1', logo: 'plugins/twilio.png', - description: 'With Twilio, unite communications and strengthen customer relationships across your business – from marketing and sales to customer service and operations.', + description: + 'With Twilio, unite communications and strengthen customer relationships across your business – from marketing and sales to customer service and operations.', price: 'Free', tags: 'Chat', category: 'Twilio', input_schema: JSON.stringify(input) -}; \ No newline at end of file +}; diff --git a/packages/nocodb/src/lib/noco/rest/RestApiBuilder.ts b/packages/nocodb/src/lib/noco/rest/RestApiBuilder.ts index 7ca362a3dc..a02bfd67e6 100644 --- a/packages/nocodb/src/lib/noco/rest/RestApiBuilder.ts +++ b/packages/nocodb/src/lib/noco/rest/RestApiBuilder.ts @@ -1,49 +1,59 @@ import fs from 'fs'; import path from 'path'; -import autoBind from "auto-bind"; +import autoBind from 'auto-bind'; import debug from 'debug'; import * as ejs from 'ejs'; -import {Router} from "express"; -import {glob} from "glob"; +import { Router } from 'express'; +import { glob } from 'glob'; import mkdirp from 'mkdirp'; -import {DbConfig, NcConfig} from "../../../interface/config"; -import ModelXcMetaFactory from "../../sqlMgr/code/models/xc/ModelXcMetaFactory"; -import SwaggerXc from "../../sqlMgr/code/routers/xc-ts/SwaggerXc"; -import SwaggerXcBt from "../../sqlMgr/code/routers/xc-ts/SwaggerXcBt"; -import SwaggerXcHm from "../../sqlMgr/code/routers/xc-ts/SwaggerXcHm"; -import ExpressXcTsRoutes from "../../sqlMgr/code/routes/xc-ts/ExpressXcTsRoutes"; -import ExpressXcTsRoutesBt from "../../sqlMgr/code/routes/xc-ts/ExpressXcTsRoutesBt"; -import ExpressXcTsRoutesHm from "../../sqlMgr/code/routes/xc-ts/ExpressXcTsRoutesHm"; -import NcHelp from "../../utils/NcHelp"; -import NcProjectBuilder from "../NcProjectBuilder"; -import Noco from "../Noco"; -import BaseApiBuilder, {IGNORE_TABLES} from "../common/BaseApiBuilder"; -import NcMetaIO from "../meta/NcMetaIO"; - -import {RestCtrl} from "./RestCtrl"; -import {RestCtrlBelongsTo} from "./RestCtrlBelongsTo"; -import {RestCtrlCustom} from "./RestCtrlCustom"; -import {RestCtrlHasMany} from "./RestCtrlHasMany"; -import {RestCtrlProcedure} from "./RestCtrlProcedure"; - +import { DbConfig, NcConfig } from '../../../interface/config'; +import ModelXcMetaFactory from '../../sqlMgr/code/models/xc/ModelXcMetaFactory'; +import SwaggerXc from '../../sqlMgr/code/routers/xc-ts/SwaggerXc'; +import SwaggerXcBt from '../../sqlMgr/code/routers/xc-ts/SwaggerXcBt'; +import SwaggerXcHm from '../../sqlMgr/code/routers/xc-ts/SwaggerXcHm'; +import ExpressXcTsRoutes from '../../sqlMgr/code/routes/xc-ts/ExpressXcTsRoutes'; +import ExpressXcTsRoutesBt from '../../sqlMgr/code/routes/xc-ts/ExpressXcTsRoutesBt'; +import ExpressXcTsRoutesHm from '../../sqlMgr/code/routes/xc-ts/ExpressXcTsRoutesHm'; +import NcHelp from '../../utils/NcHelp'; +import NcProjectBuilder from '../NcProjectBuilder'; +import Noco from '../Noco'; +import BaseApiBuilder, { IGNORE_TABLES } from '../common/BaseApiBuilder'; +import NcMetaIO from '../meta/NcMetaIO'; + +import { RestCtrl } from './RestCtrl'; +import { RestCtrlBelongsTo } from './RestCtrlBelongsTo'; +import { RestCtrlCustom } from './RestCtrlCustom'; +import { RestCtrlHasMany } from './RestCtrlHasMany'; +import { RestCtrlProcedure } from './RestCtrlProcedure'; const log = debug('nc:api:rest'); const NC_CUSTOM_ROUTE_KEY = '__xc_custom'; export class RestApiBuilder extends BaseApiBuilder { public readonly type = 'rest'; - private controllers: { [key: string]: RestCtrlBelongsTo | RestCtrl | RestCtrlHasMany | RestCtrlCustom }; + private controllers: { + [key: string]: + | RestCtrlBelongsTo + | RestCtrl + | RestCtrlHasMany + | RestCtrlCustom; + }; private procedureCtrl: RestCtrlProcedure; private routers: { [key: string]: Router }; private apiCount = 0; private customRoutes: any; - - constructor(app: Noco, projectBuilder: NcProjectBuilder, config: NcConfig, connectionConfig: DbConfig, xcMeta?: NcMetaIO) { + constructor( + app: Noco, + projectBuilder: NcProjectBuilder, + config: NcConfig, + connectionConfig: DbConfig, + xcMeta?: NcMetaIO + ) { super(app, projectBuilder, config, connectionConfig); - autoBind(this) + autoBind(this); this.controllers = {}; this.routers = {}; this.hooks = {}; @@ -58,7 +68,7 @@ export class RestApiBuilder extends BaseApiBuilder { public async loadRoutes(customRoutes: any): Promise { this.customRoutes = customRoutes; - this.log('loadRoutes') + this.log('loadRoutes'); const t = process.hrtime(); this.initDbDriver(); @@ -66,16 +76,16 @@ export class RestApiBuilder extends BaseApiBuilder { // todo: change condition if (this.connectionConfig.meta.reset) { await this.xcMeta.metaReset(this.projectId, this.dbAlias); - this.log('loadRoutes : Metadata reset completed') + this.log('loadRoutes : Metadata reset completed'); } - if (!await this.xcMeta.isMetaDataExists(this.projectId, this.dbAlias)) { - await this.xcTablesPopulate({}) - this.log('loadRoutes : Populated metadata from database') + if (!(await this.xcMeta.isMetaDataExists(this.projectId, this.dbAlias))) { + await this.xcTablesPopulate({}); + this.log('loadRoutes : Populated metadata from database'); } else { // NOTE: xc-meta await this.xcTablesRead(); - this.log('loadRoutes : App initialized from metadata') + this.log('loadRoutes : App initialized from metadata'); } await this.loadHooks(); @@ -86,7 +96,7 @@ export class RestApiBuilder extends BaseApiBuilder { const t2 = t1[0] + t1[1] / 1000000000; return { - type: "rest", + type: 'rest', apiCount: this.apiCount, dbType: this.connectionConfig.client, apiEndpoint: `/nc/${this.projectId}/${this.dbAlias}/swagger`, @@ -101,76 +111,94 @@ export class RestApiBuilder extends BaseApiBuilder { }; } - public async xcTablesRead(tables?: string[]): Promise { - this.log('xcTablesRead : %o', tables) + this.log('xcTablesRead : %o', tables); const swagger = []; - const {enabledModels, tableAndViewArr, functionArr, procedureArr} = await this.readXcModelsAndGroupByType(); - - const router = this.routers.___procedure = Router(); - this.procedureCtrl = new RestCtrlProcedure(this, functionArr, procedureArr, this.procedureOrFunctionAcls); + const { + enabledModels, + tableAndViewArr, + functionArr, + procedureArr + } = await this.readXcModelsAndGroupByType(); + + const router = (this.routers.___procedure = Router()); + this.procedureCtrl = new RestCtrlProcedure( + this, + functionArr, + procedureArr, + this.procedureOrFunctionAcls + ); this.procedureCtrl.mapRoutes(router, this.customRoutes); - swagger.push(this.procedureCtrl.getSwaggerObj()) + swagger.push(this.procedureCtrl.getSwaggerObj()); this.router.use('/api/' + this.routeVersionLetter, router); - - const routesArr = (await this.xcMeta.metaList(this.projectId, this.dbAlias, 'nc_routes', { - condition: { - handler_type: 1 - }, - xcCondition: { - _not: { - is_custom: true + const routesArr = ( + await this.xcMeta.metaList(this.projectId, this.dbAlias, 'nc_routes', { + condition: { + handler_type: 1 + }, + xcCondition: { + _not: { + is_custom: true + } } - }, - })) + }) + ) .sort((a, b) => a.order - b.order) .map(it => ({ - ...it, - acl: JSON.parse(it.acl), - functions: it.functions && JSON.parse(it.functions), - handler: JSON.parse(it.handler) - }) - ); + ...it, + acl: JSON.parse(it.acl), + functions: it.functions && JSON.parse(it.functions), + handler: JSON.parse(it.handler) + })); - const middlewaresArr = (await this.xcMeta.metaList(this.projectId, this.dbAlias, 'nc_routes', { - condition: {handler_type: 2}, - xcCondition: { - _not: { - is_custom: true + const middlewaresArr = ( + await this.xcMeta.metaList(this.projectId, this.dbAlias, 'nc_routes', { + condition: { handler_type: 2 }, + xcCondition: { + _not: { + is_custom: true + } } - }, - })) + }) + ) .sort((a, b) => a.order - b.order) .map(it => ({ - ...it, - functions: it.functions && JSON.parse(it.functions), - }) - ); + ...it, + functions: it.functions && JSON.parse(it.functions) + })); - const customRoutes = (await this.xcMeta.metaList(this.projectId, this.dbAlias, 'nc_routes', { - condition: { - is_custom: true - }, - })) + const customRoutes = ( + await this.xcMeta.metaList(this.projectId, this.dbAlias, 'nc_routes', { + condition: { + is_custom: true + } + }) + ) .sort((a, b) => a.order - b.order) .map(it => ({ - ...it, - functions: it.functions && JSON.parse(it.functions), - }) - ); - + ...it, + functions: it.functions && JSON.parse(it.functions) + })); const relationRoutes = []; - await this.loadXcAcl(); for (const meta of tableAndViewArr) { - this.log('xcTablesRead : Adding routes for \'%s\' - %s', meta.title, meta.type); - const middlewareBody = middlewaresArr.find(({title}) => title === meta.title)?.functions?.[0]; - - if (!enabledModels.includes(meta.title) || (tables && !tables.includes(meta.title))) { + this.log( + "xcTablesRead : Adding routes for '%s' - %s", + meta.title, + meta.type + ); + const middlewareBody = middlewaresArr.find( + ({ title }) => title === meta.title + )?.functions?.[0]; + + if ( + !enabledModels.includes(meta.title) || + (tables && !tables.includes(meta.title)) + ) { continue; } @@ -179,13 +207,19 @@ export class RestApiBuilder extends BaseApiBuilder { this.metas[meta.title] = metaObj; this.models[meta.title] = this.getBaseModel(metaObj); try { - this.log('xcTablesRead : Parsing swagger doc of \'%s\' %s', meta.title, meta.type); - swagger.push(JSON.parse(meta.schema)) + this.log( + "xcTablesRead : Parsing swagger doc of '%s' %s", + meta.title, + meta.type + ); + swagger.push(JSON.parse(meta.schema)); } catch (e) { - console.log('Failed swagger doc parsing of \'' + meta.title + "' " + meta.type) + console.log( + "Failed swagger doc parsing of '" + meta.title + "' " + meta.type + ); } - const routes = routesArr.filter(({title}) => title === meta.title); - const router = this.routers[meta.title] = Router(); + const routes = routesArr.filter(({ title }) => title === meta.title); + const router = (this.routers[meta.title] = Router()); const rootPath = routes?.[0]?.path?.match(/\/api\/[^/]+\/[^/]+/)?.[0]; if (!rootPath) { continue; @@ -193,10 +227,17 @@ export class RestApiBuilder extends BaseApiBuilder { this.router.use(encodeURI(rootPath), router); this.apiCount += routes.length; - this.controllers[meta.title] = new RestCtrl(this.app, this.models, meta.title, routes, rootPath, this.acls, middlewareBody); + this.controllers[meta.title] = new RestCtrl( + this.app, + this.models, + meta.title, + routes, + rootPath, + this.acls, + middlewareBody + ); this.controllers[meta.title].mapRoutes(router, this.customRoutes); - relationRoutes.push(async () => { for (const hm of metaObj.hasMany) { if (!enabledModels.includes(hm.tn) || !hm.enabled) { @@ -204,11 +245,22 @@ export class RestApiBuilder extends BaseApiBuilder { } const name = `${meta.title}Hm${hm.tn}`; - const hmRoutes = routesArr.filter(({title}) => title === name); - const hmMiddlewareBody = middlewaresArr.find(({title}) => title === name)?.functions?.[0]; + const hmRoutes = routesArr.filter(({ title }) => title === name); + const hmMiddlewareBody = middlewaresArr.find( + ({ title }) => title === name + )?.functions?.[0]; this.apiCount += hmRoutes.length; - this.controllers[name] = new RestCtrlHasMany(this.app, this.models, meta.title, hm.tn, hmRoutes, rootPath, this.acls, hmMiddlewareBody); + this.controllers[name] = new RestCtrlHasMany( + this.app, + this.models, + meta.title, + hm.tn, + hmRoutes, + rootPath, + this.acls, + hmMiddlewareBody + ); this.controllers[name].mapRoutes(router, this.customRoutes); } for (const bt of metaObj.belongsTo) { @@ -219,13 +271,24 @@ export class RestApiBuilder extends BaseApiBuilder { const name = `${meta.title}Bt${bt.rtn}`; - this.log('xcTablesRead : Adding routes for \'%s\' - relation', name); + this.log("xcTablesRead : Adding routes for '%s' - relation", name); - const btRoutes = routesArr.filter(({title}) => title === name); - const btMiddlewareBody = middlewaresArr.find(({title}) => title === name)?.functions?.[0]; + const btRoutes = routesArr.filter(({ title }) => title === name); + const btMiddlewareBody = middlewaresArr.find( + ({ title }) => title === name + )?.functions?.[0]; this.apiCount += btRoutes.length; - this.controllers[name] = new RestCtrlBelongsTo(this.app, this.models, bt.rtn, bt.tn, btRoutes, rootPath, this.acls, btMiddlewareBody); + this.controllers[name] = new RestCtrlBelongsTo( + this.app, + this.models, + bt.rtn, + bt.tn, + btRoutes, + rootPath, + this.acls, + btMiddlewareBody + ); this.controllers[name].mapRoutes(router, this.customRoutes); } }); @@ -233,19 +296,25 @@ export class RestApiBuilder extends BaseApiBuilder { await Promise.all(relationRoutes.map(async f => f())); - if (customRoutes.length) { - const customRouter = this.routers[NC_CUSTOM_ROUTE_KEY] = Router(); - this.router.use(customRouter) - this.controllers[NC_CUSTOM_ROUTE_KEY] = new RestCtrlCustom(this.app, this.models, customRoutes); - this.controllers[NC_CUSTOM_ROUTE_KEY].mapRoutes(customRouter, this.customRoutes); + const customRouter = (this.routers[NC_CUSTOM_ROUTE_KEY] = Router()); + this.router.use(customRouter); + this.controllers[NC_CUSTOM_ROUTE_KEY] = new RestCtrlCustom( + this.app, + this.models, + customRoutes + ); + this.controllers[NC_CUSTOM_ROUTE_KEY].mapRoutes( + customRouter, + this.customRoutes + ); } const swaggerDoc = { tags: [], paths: {}, definitions: {} - } + }; swagger.forEach(swaggerJson => { if (swaggerJson) { @@ -258,7 +327,7 @@ export class RestApiBuilder extends BaseApiBuilder { if (tables?.length) { this.swaggerUpdate({ addApis: swaggerDoc - }) + }); } else { /* generate swagger docs */ await this.generateSwaggerJson(swaggerDoc); @@ -266,21 +335,23 @@ export class RestApiBuilder extends BaseApiBuilder { // const minRouter = new RestCtrlMin(this.app,this.models,this.acls); // minRouter.mapRoutes(this.router) - } - public async xcTablesPopulate(args?: { tableNames?: Array<{ tn: string; _tn?: string; }>; - type?: 'table' | 'view' | 'function' | 'procedure', + type?: 'table' | 'view' | 'function' | 'procedure'; columns?: { - [tn: string]: any - } + [tn: string]: any; + }; }): Promise { - this.log(`xcTablesPopulate : tables - %o , type - %s`, args?.tableNames, args?.type); + this.log( + `xcTablesPopulate : tables - %o , type - %s`, + args?.tableNames, + args?.type + ); let tables; const swaggerRefs: { [table: string]: any[] } = {}; @@ -289,12 +360,15 @@ export class RestApiBuilder extends BaseApiBuilder { // set table name alias relations.forEach(r => { - r._rtn = args?.tableNames?.find(t => t.tn === r.rtn)?._tn || this.getTableNameAlias(r.rtn); - r._tn = args?.tableNames?.find(t => t.tn === r.tn)?._tn || this.getTableNameAlias(r.tn); + r._rtn = + args?.tableNames?.find(t => t.tn === r.rtn)?._tn || + this.getTableNameAlias(r.rtn); + r._tn = + args?.tableNames?.find(t => t.tn === r.tn)?._tn || + this.getTableNameAlias(r.tn); r.enabled = true; }); - this.relationsCount = relations.length; if (args?.tableNames?.length) { @@ -304,22 +378,27 @@ export class RestApiBuilder extends BaseApiBuilder { for (const r of relations) { if (args.tableNames.some(t => t.tn === r.tn)) { if (!relatedTableList.includes(r.rtn)) { - relatedTableList.push(r.rtn) - await this.onTableDelete(r.rtn) + relatedTableList.push(r.rtn); + await this.onTableDelete(r.rtn); } } else if (args.tableNames.some(t => t.tn === r.rtn)) { if (!relatedTableList.includes(r.tn)) { - relatedTableList.push(r.tn) - await this.onTableDelete(r.tn) + relatedTableList.push(r.tn); + await this.onTableDelete(r.tn); } } } - tables = args.tableNames.map(({tn, _tn}) => ({tn, type: args.type, _tn})); - tables.push(...relatedTableList.map(t => ({tn: t}))) + tables = args.tableNames.map(({ tn, _tn }) => ({ + tn, + type: args.type, + _tn + })); + tables.push(...relatedTableList.map(t => ({ tn: t }))); } else { - tables = (await this.sqlClient.tableList())?.data?.list?.filter(({tn}) => !IGNORE_TABLES.includes(tn)); - + tables = (await this.sqlClient.tableList())?.data?.list?.filter( + ({ tn }) => !IGNORE_TABLES.includes(tn) + ); // enable extra /* tables.push(...(await this.sqlClient.viewList())?.data?.list?.map(v => { @@ -339,13 +418,12 @@ export class RestApiBuilder extends BaseApiBuilder { await this.populteProcedureAndFunctionRoutes(); } - /* filter based on prefix */ if (this.projectBuilder?.prefix) { tables = tables.filter(t => { - t._tn = t._tn || t.tn.replace(this.projectBuilder?.prefix, '') - return t?.tn?.startsWith(this.projectBuilder?.prefix) - }) + t._tn = t._tn || t.tn.replace(this.projectBuilder?.prefix, ''); + return t?.tn?.startsWith(this.projectBuilder?.prefix); + }); } this.tablesCount = tables.length; @@ -354,88 +432,162 @@ export class RestApiBuilder extends BaseApiBuilder { relations.forEach(r => { r._tn = this.getTableNameAlias(r.tn); - r._rtn = this.getTableNameAlias(r.rtn) + r._rtn = this.getTableNameAlias(r.rtn); }); - const tableRoutes = tables.map(table => { return async () => { swaggerRefs[table.tn] = []; - this.log('xcTablesPopulate : Generating metadata for \'%s\' - %s', table.tn, table.type) + this.log( + "xcTablesPopulate : Generating metadata for '%s' - %s", + table.tn, + table.type + ); /* filter relation where this table is present */ - const tableRelations = relations.filter(r => ( - r.tn === table.tn || r.rtn === table.tn) + const tableRelations = relations.filter( + r => r.tn === table.tn || r.rtn === table.tn ); - - const columns = args?.columns?.[table.tn] || (await this.sqlClient.columnList({tn: table.tn}))?.data?.list; - - const hasMany = table.type === 'view' ? [] : JSON.parse(JSON.stringify(tableRelations.filter(r => (r.rtn === table.tn)))); - const belongsTo = table.type === 'view' ? [] : JSON.parse(JSON.stringify(tableRelations.filter(r => (r.tn === table.tn)))); - - const ctx = this.generateContextForTable(table.tn, columns, table.type === 'view' ? [] : relations, hasMany, belongsTo, table.type, args.tableNames?.find(t => t.tn === table.tn)?._tn); + const columns = + args?.columns?.[table.tn] || + (await this.sqlClient.columnList({ tn: table.tn }))?.data?.list; + + const hasMany = + table.type === 'view' + ? [] + : JSON.parse( + JSON.stringify(tableRelations.filter(r => r.rtn === table.tn)) + ); + const belongsTo = + table.type === 'view' + ? [] + : JSON.parse( + JSON.stringify(tableRelations.filter(r => r.tn === table.tn)) + ); + + const ctx = this.generateContextForTable( + table.tn, + columns, + table.type === 'view' ? [] : relations, + hasMany, + belongsTo, + table.type, + args.tableNames?.find(t => t.tn === table.tn)?._tn + ); // ctx._tn = args.tableNames?.find(t => t.tn === table.tn)?._tn || ctx._tn; /* create models from table metadata */ - const meta = ModelXcMetaFactory.create(this.connectionConfig, {dir: '', ctx, filename: ''}).getObject(); + const meta = ModelXcMetaFactory.create(this.connectionConfig, { + dir: '', + ctx, + filename: '' + }).getObject(); /* create nc_models and its rows if it doesn't exists */ - if (!(await this.xcMeta.metaGet(this.projectId, this.dbAlias, 'nc_models', {title: table.tn}))) { - this.log('xcTablesPopulate : Inserting model metadata for \'%s\' - %s', table.tn, table.type) - await this.xcMeta.metaInsert(this.projectId, this.dbAlias, 'nc_models', { - title: table.tn, - alias: meta._tn, - meta: JSON.stringify(meta), - type: table.type || 'table' - }) + if ( + !(await this.xcMeta.metaGet( + this.projectId, + this.dbAlias, + 'nc_models', + { title: table.tn } + )) + ) { + this.log( + "xcTablesPopulate : Inserting model metadata for '%s' - %s", + table.tn, + table.type + ); + await this.xcMeta.metaInsert( + this.projectId, + this.dbAlias, + 'nc_models', + { + title: table.tn, + alias: meta._tn, + meta: JSON.stringify(meta), + type: table.type || 'table' + } + ); } - /* create routes for table */ - const routes = new ExpressXcTsRoutes({dir: '', ctx, filename: ''}).getObjectWithoutFunctions(); - - - this.log('xcTablesPopulate : Generating swagger apis for \'%s\' - %s', table.tn, table.type) - /* create swagger json for table */ - swaggerRefs[table.tn].push(await new SwaggerXc({ + const routes = new ExpressXcTsRoutes({ dir: '', - ctx: { - ...ctx, - v: meta.v - }, filename: '' - }).getObject()) + ctx, + filename: '' + }).getObjectWithoutFunctions(); + + this.log( + "xcTablesPopulate : Generating swagger apis for '%s' - %s", + table.tn, + table.type + ); + /* create swagger json for table */ + swaggerRefs[table.tn].push( + await new SwaggerXc({ + dir: '', + ctx: { + ...ctx, + v: meta.v + }, + filename: '' + }).getObject() + ); await this.generateAndSaveAcl(table.tn, table.type); /* create nc_routes and its rows if it doesn't exists */ - if (!(await this.xcMeta.metaGet(this.projectId, this.dbAlias, 'nc_routes', {'title': table.tn}))) { + if ( + !(await this.xcMeta.metaGet( + this.projectId, + this.dbAlias, + 'nc_routes', + { title: table.tn } + )) + ) { const routesInsertion = routes.map((route, i) => { return async () => { - await this.xcMeta.metaInsert(this.projectId, this.dbAlias, 'nc_routes', { - acl: JSON.stringify(route.acl), - handler: JSON.stringify(route.handler), - order: i, - path: route.path, - tn: table.tn, - title: table.tn, - type: route.type, - }) - } + await this.xcMeta.metaInsert( + this.projectId, + this.dbAlias, + 'nc_routes', + { + acl: JSON.stringify(route.acl), + handler: JSON.stringify(route.handler), + order: i, + path: route.path, + tn: table.tn, + title: table.tn, + type: route.type + } + ); + }; }); routesInsertion.push(async () => { - await this.xcMeta.metaInsert(this.projectId, this.dbAlias, 'nc_routes', { - tn: table.tn, - title: table.tn, - handler_type: 2 - }) - }) - - this.log('xcTablesPopulate : Inserting routes and middleware metadata for \'%s\' - %s', table.tn, table.type) - await NcHelp.executeOperations(routesInsertion, this.connectionConfig.client); + await this.xcMeta.metaInsert( + this.projectId, + this.dbAlias, + 'nc_routes', + { + tn: table.tn, + title: table.tn, + handler_type: 2 + } + ); + }); + this.log( + "xcTablesPopulate : Inserting routes and middleware metadata for '%s' - %s", + table.tn, + table.type + ); + await NcHelp.executeOperations( + routesInsertion, + this.connectionConfig.client + ); } this.metas[table.tn] = meta; @@ -444,139 +596,208 @@ export class RestApiBuilder extends BaseApiBuilder { this.apiCount += routes.length; - const router = this.routers[table.tn] = Router(); + const router = (this.routers[table.tn] = Router()); const rootPath = `/api/${ctx.routeVersionLetter}/${ctx._tn}`; - /* create table controllers and map the routes */ - this.controllers[table.tn] = new RestCtrl(this.app, this.models, table.tn, routes, rootPath, this.acls, null); + this.controllers[table.tn] = new RestCtrl( + this.app, + this.models, + table.tn, + routes, + rootPath, + this.acls, + null + ); this.controllers[table.tn].mapRoutes(router, this.customRoutes); this.router.use(encodeURI(rootPath), router); /* handle relational routes */ relationRoutes.push(async () => { for (const hm of meta.hasMany) { - const hmCtx = this.generateContextForHasMany(ctx, hm.tn); - const hmRoutes = new ExpressXcTsRoutesHm(this.generateRendererArgs(hmCtx)).getObjectWithoutFunctions(); + const hmRoutes = new ExpressXcTsRoutesHm( + this.generateRendererArgs(hmCtx) + ).getObjectWithoutFunctions(); /* create swagger json for hasmany */ - swaggerRefs[table.tn].push(await new SwaggerXcHm(this.generateRendererArgs(this.generateContextForHasMany(hmCtx, hm.tn))).getObject()); - + swaggerRefs[table.tn].push( + await new SwaggerXcHm( + this.generateRendererArgs( + this.generateContextForHasMany(hmCtx, hm.tn) + ) + ).getObject() + ); const name = `${table.tn}Hm${hm.tn}`; /* handle has many relational routes */ const hmRoutesInsertion = hmRoutes.map((route, i) => { return async () => { - await this.xcMeta.metaInsert(this.projectId, this.dbAlias, 'nc_routes', { - title: name, - tn: table.tn, - order: i, - path: route.path, - type: route.type, - handler: JSON.stringify(route.handler), - acl: JSON.stringify(route.acl), - relation_type: 'hasMany', - tnc: hm.tn - }) - } + await this.xcMeta.metaInsert( + this.projectId, + this.dbAlias, + 'nc_routes', + { + title: name, + tn: table.tn, + order: i, + path: route.path, + type: route.type, + handler: JSON.stringify(route.handler), + acl: JSON.stringify(route.acl), + relation_type: 'hasMany', + tnc: hm.tn + } + ); + }; }); this.apiCount += hmRoutes.length; hmRoutesInsertion.push(async () => { - await this.xcMeta.metaInsert(this.projectId, this.dbAlias, 'nc_routes', { - tn: table.tn, - title: name, - handler_type: 2, - relation_type: 'hasMany', - tnc: hm.tn - }) + await this.xcMeta.metaInsert( + this.projectId, + this.dbAlias, + 'nc_routes', + { + tn: table.tn, + title: name, + handler_type: 2, + relation_type: 'hasMany', + tnc: hm.tn + } + ); }); - await NcHelp.executeOperations(hmRoutesInsertion, this.connectionConfig.client); + await NcHelp.executeOperations( + hmRoutesInsertion, + this.connectionConfig.client + ); if (!hm.enabled) { continue; } /* create and map has many routes */ - this.controllers[name] = new RestCtrlHasMany(this.app, this.models, table.tn, hm.tn, hmRoutes, rootPath, this.acls); + this.controllers[name] = new RestCtrlHasMany( + this.app, + this.models, + table.tn, + hm.tn, + hmRoutes, + rootPath, + this.acls + ); this.controllers[name].mapRoutes(router, this.customRoutes); } /* handle belongs tou routes and controllers */ for (const bt of meta.belongsTo) { - - const btCtx = this.generateContextForBelongsTo(ctx, bt.rtn); - const btRoutes = new ExpressXcTsRoutesBt(this.generateRendererArgs(btCtx)).getObjectWithoutFunctions(); + const btRoutes = new ExpressXcTsRoutesBt( + this.generateRendererArgs(btCtx) + ).getObjectWithoutFunctions(); /* create swagger json for hasmany */ - swaggerRefs[table.tn].push(await new SwaggerXcBt(this.generateRendererArgs(this.generateContextForBelongsTo(btCtx, bt.rtn))).getObject()); - + swaggerRefs[table.tn].push( + await new SwaggerXcBt( + this.generateRendererArgs( + this.generateContextForBelongsTo(btCtx, bt.rtn) + ) + ).getObject() + ); const name = `${table.tn}Bt${bt.rtn}`; this.apiCount += btRoutes.length; const btRoutesInsertion = btRoutes.map((route, i) => { return async () => { - await this.xcMeta.metaInsert(this.projectId, this.dbAlias, 'nc_routes', { + await this.xcMeta.metaInsert( + this.projectId, + this.dbAlias, + 'nc_routes', + { + tn: table.tn, + title: name, + order: i, + path: route.path, + type: route.type, + handler: JSON.stringify(route.handler), + acl: JSON.stringify(route.acl), + relation_type: 'belongsTo', + tnp: bt.rtn + } + ); + }; + }); + + btRoutesInsertion.push(async () => { + await this.xcMeta.metaInsert( + this.projectId, + this.dbAlias, + 'nc_routes', + { tn: table.tn, title: name, - order: i, - path: route.path, - type: route.type, - handler: JSON.stringify(route.handler), - acl: JSON.stringify(route.acl), + handler_type: 2, relation_type: 'belongsTo', tnp: bt.rtn - }) - } - }); - - btRoutesInsertion.push(async () => { - await this.xcMeta.metaInsert(this.projectId, this.dbAlias, 'nc_routes', { - tn: table.tn, - title: name, - handler_type: 2, - relation_type: 'belongsTo', - tnp: bt.rtn - }) + } + ); }); - await NcHelp.executeOperations(btRoutesInsertion, this.connectionConfig.client); - + await NcHelp.executeOperations( + btRoutesInsertion, + this.connectionConfig.client + ); if (!bt.enabled) { continue; } /* create and map belongs to routes */ - this.controllers[name] = new RestCtrlBelongsTo(this.app, this.models, bt.rtn, bt.tn, btRoutes, rootPath, this.acls); + this.controllers[name] = new RestCtrlBelongsTo( + this.app, + this.models, + bt.rtn, + bt.tn, + btRoutes, + rootPath, + this.acls + ); this.controllers[name].mapRoutes(router, this.customRoutes); } }); - } + }; }); /* handle xc_tables update in parallel */ await NcHelp.executeOperations(tableRoutes, this.connectionConfig.client); - await NcHelp.executeOperations(relationRoutes, this.connectionConfig.client); - + await NcHelp.executeOperations( + relationRoutes, + this.connectionConfig.client + ); const swaggerDoc = { tags: [], paths: {}, definitions: {} - } + }; - this.log('xcTablesPopulate : Merging and saving all the swagger objects') + this.log('xcTablesPopulate : Merging and saving all the swagger objects'); - for (const [table, swagger] of [...Object.entries(swaggerRefs), [null, [this.procedureCtrl?.getSwaggerObj() || { - tags: [], - path: {} - }]]]) { + for (const [table, swagger] of [ + ...Object.entries(swaggerRefs), + [ + null, + [ + this.procedureCtrl?.getSwaggerObj() || { + tags: [], + path: {} + } + ] + ] + ]) { if (table) { await this.mergeAndUpdateSwagger(table as string, swagger); } @@ -591,18 +812,15 @@ export class RestApiBuilder extends BaseApiBuilder { if (args.tableNames && args.tableNames.length) { this.swaggerUpdate({ addApis: swaggerDoc - }) + }); } else { /* generate swagger docs */ await this.generateSwaggerJson(swaggerDoc); } - await this.getManyToManyRelations(); - } - // NOTE: xc-meta public async xcTablesRowDelete(tn: string): Promise { await super.xcTablesRowDelete(tn); @@ -612,105 +830,129 @@ export class RestApiBuilder extends BaseApiBuilder { } public async onTableCreate(tn: string, args?: any): Promise { - await super.onTableCreate(tn, args); - const columns = args.columns ? { - [tn]: args.columns?.map(({altered: _al, ...rest}) => rest) - } : {} - + const columns = args.columns + ? { + [tn]: args.columns?.map(({ altered: _al, ...rest }) => rest) + } + : {}; - await this.xcTablesPopulate({tableNames: [{tn, _tn: args._tn}], columns}); + await this.xcTablesPopulate({ + tableNames: [{ tn, _tn: args._tn }], + columns + }); } public async onTableDelete(tn: string): Promise { await super.onTableDelete(tn); - this.log('onTableDelete : \'%s\'', tn) + this.log("onTableDelete : '%s'", tn); try { const ctrlIndex = this.router.stack.findIndex(r => { return r.handle === this.routers[tn]; - }) + }); if (ctrlIndex > -1) { - this.router.stack.splice(ctrlIndex, 1) + this.router.stack.splice(ctrlIndex, 1); } delete this.models[tn]; - await this.xcTablesRowDelete(tn) + await this.xcTablesRowDelete(tn); delete this.routers[tn]; this.swaggerUpdate({ deleteTags: [tn] - }) + }); } catch (e) { console.log(e); } } - public async onViewDelete(viewName: string): Promise { - this.log('onViewDelete : \'%s\'', viewName) + this.log("onViewDelete : '%s'", viewName); try { const ctrlIndex = this.router.stack.findIndex(r => { return r.handle === this.routers[viewName]; - }) + }); if (ctrlIndex > -1) { - this.router.stack.splice(ctrlIndex, 1) + this.router.stack.splice(ctrlIndex, 1); } delete this.models[viewName]; - await this.xcTablesRowDelete(viewName) - + await this.xcTablesRowDelete(viewName); delete this.routers[viewName]; this.swaggerUpdate({ deleteTags: [viewName] - }) + }); } catch (e) { console.log(e); } } - public async onHandlerCodeUpdate(tn: string): Promise { - this.log('onHandlerCodeUpdate : \'%s\'', tn) - const index = this.router.stack.findIndex(r => r.handle === this.routers[tn]); + this.log("onHandlerCodeUpdate : '%s'", tn); + const index = this.router.stack.findIndex( + r => r.handle === this.routers[tn] + ); if (index > -1) { this.router.stack.splice(index, 1); } await this.xcTablesRead([tn]); } - - public async onTableRename(oldTableName: string, newTableName: string): Promise { - this.log('onTableRename : \'%s\' => \'%s\'', oldTableName, newTableName) + public async onTableRename( + oldTableName: string, + newTableName: string + ): Promise { + this.log("onTableRename : '%s' => '%s'", oldTableName, newTableName); await super.onTableRename(oldTableName, newTableName); await this.xcTableRename(oldTableName, newTableName); // await this.onTableDelete(oldTableName); // await this.xcTablesPopulate({tableNames: [newTableName]}); } - // NOTE: xc-meta - public async xcTableRename(oldTablename: string, newTablename: string): Promise { - - this.log('xcTableRename : \'%s\' => \'%s\' ', oldTablename, newTablename) + public async xcTableRename( + oldTablename: string, + newTablename: string + ): Promise { + this.log("xcTableRename : '%s' => '%s' ", oldTablename, newTablename); const tables = []; const newSwagger = []; - /* Get all relations */ const relations = await this.getRelationList(); - const relatedTableList = this.getRelationTableNames(relations, newTablename); + const relatedTableList = this.getRelationTableNames( + relations, + newTablename + ); /* filter relation where this table is present */ - const tableRelations = this.filterRelationsForTable(relations, newTablename); - const hasMany = this.extractHasManyRelationsOfTable(tableRelations, newTablename); - const belongsTo = this.extractBelongsToRelationsOfTable(tableRelations, newTablename); + const tableRelations = this.filterRelationsForTable( + relations, + newTablename + ); + const hasMany = this.extractHasManyRelationsOfTable( + tableRelations, + newTablename + ); + const belongsTo = this.extractBelongsToRelationsOfTable( + tableRelations, + newTablename + ); const columns = await this.getColumnList(newTablename); - const ctx = this.generateContextForTable(newTablename, columns, relations, hasMany, belongsTo); + const ctx = this.generateContextForTable( + newTablename, + columns, + relations, + hasMany, + belongsTo + ); - newSwagger.push(await new SwaggerXc({dir: '', ctx, filename: ''}).getObject()); + newSwagger.push( + await new SwaggerXc({ dir: '', ctx, filename: '' }).getObject() + ); this.deleteRoutesForTables([oldTablename, ...relatedTableList]); // delete old model @@ -722,27 +964,42 @@ export class RestApiBuilder extends BaseApiBuilder { // const rootPath = `/api/${ctx.routeVersionLetter}/${ctx._tn}` /* create models from table */ - meta = ModelXcMetaFactory.create(this.connectionConfig, this.generateRendererArgs(ctx)).getObject(); + meta = ModelXcMetaFactory.create( + this.connectionConfig, + this.generateRendererArgs(ctx) + ).getObject(); // update old model meta with new details // const existingModel = await this.sqlClient.knex('nc_models').where('title', oldTablename).first(); - const existingModel = await this.xcMeta.metaGet(this.projectId, this.dbAlias, 'nc_models', {'title': oldTablename}); - + const existingModel = await this.xcMeta.metaGet( + this.projectId, + this.dbAlias, + 'nc_models', + { title: oldTablename } + ); if (existingModel) { // todo: persisting old table_alias and columnAlias const oldMeta = JSON.parse(existingModel.meta); Object.assign(meta, { - columns: oldMeta.columns, + columns: oldMeta.columns }); - this.log('Updating table name in \'nc_models\' metatable - \'%s\' => \'%s\'', oldTablename, newTablename) - await this.xcMeta.metaUpdate(this.projectId, this.dbAlias, 'nc_models', { - title: newTablename, - meta: JSON.stringify(meta), - alias: meta._tn, - }, {'title': oldTablename}) - - + this.log( + "Updating table name in 'nc_models' metatable - '%s' => '%s'", + oldTablename, + newTablename + ); + await this.xcMeta.metaUpdate( + this.projectId, + this.dbAlias, + 'nc_models', + { + title: newTablename, + meta: JSON.stringify(meta), + alias: meta._tn + }, + { title: oldTablename } + ); } this.metas[newTablename] = meta; @@ -750,42 +1007,68 @@ export class RestApiBuilder extends BaseApiBuilder { } // update tn in nc_acl - this.log('xcTableRename : Updating table name in \'nc_acl\' metatable - \'%s\' => \'%s\'', oldTablename, newTablename) - await this.xcMeta.metaUpdate(this.projectId, this.dbAlias, 'nc_acl', { - tn: newTablename - }, { - tn: oldTablename - }); - + this.log( + "xcTableRename : Updating table name in 'nc_acl' metatable - '%s' => '%s'", + oldTablename, + newTablename + ); + await this.xcMeta.metaUpdate( + this.projectId, + this.dbAlias, + 'nc_acl', + { + tn: newTablename + }, + { + tn: oldTablename + } + ); /* create routes from table */ - const routes = new ExpressXcTsRoutes({dir: '', ctx, filename: ''}).getObjectWithoutFunctions(); + const routes = new ExpressXcTsRoutes({ + dir: '', + ctx, + filename: '' + }).getObjectWithoutFunctions(); const routesUpdate = routes.map((route, i) => { return async () => { - await this.xcMeta.metaUpdate(this.projectId, this.dbAlias, 'nc_routes', { - path: route.path, - tn: newTablename, - title: newTablename - }, { - handler: JSON.stringify(route.handler), - tn: oldTablename, - title: oldTablename, - type: route.type, - order: i, - }) - } + await this.xcMeta.metaUpdate( + this.projectId, + this.dbAlias, + 'nc_routes', + { + path: route.path, + tn: newTablename, + title: newTablename + }, + { + handler: JSON.stringify(route.handler), + tn: oldTablename, + title: oldTablename, + type: route.type, + order: i + } + ); + }; }); - this.log('xcTableRename : Updating table name and route path in \'nc_routes\' metatable - \'%s\' => \'%s\'', oldTablename, newTablename) + this.log( + "xcTableRename : Updating table name and route path in 'nc_routes' metatable - '%s' => '%s'", + oldTablename, + newTablename + ); await NcHelp.executeOperations(routesUpdate, this.connectionConfig.client); /* handle relational routes */ routesUpdate.push(async () => { for (const hm of meta.hasMany) { - - const rendererArgs = this.generateRendererArgs(this.generateContextForHasMany(ctx, hm.tn)); - const hmRoutes = new ExpressXcTsRoutesHm(rendererArgs).getObjectWithoutFunctions(); + const rendererArgs = this.generateRendererArgs( + this.generateContextForHasMany(ctx, hm.tn) + ); + const hmRoutes = new ExpressXcTsRoutesHm( + rendererArgs + ).getObjectWithoutFunctions(); newSwagger.push(new SwaggerXcHm(rendererArgs).getObject()); const oldName = `${oldTablename}Hm${hm.tn}`; @@ -794,50 +1077,71 @@ export class RestApiBuilder extends BaseApiBuilder { /* handle has many relational routes */ const hmRoutesInsertion = hmRoutes.map((route, i) => { return async () => { - - this.log('xcTableRename : Updating table name and route path in \'nc_routes\' metatable - \'%s\' => \'%s\' (HasMany relation)', oldTablename, newTablename) - await this.xcMeta.metaUpdate(this.projectId, this.dbAlias, 'nc_routes', { - path: route.path, - tn: newTablename, - title: name, - }, { - title: oldName, - handler: JSON.stringify(route.handler), - tn: oldTablename, - type: route.type, - relation_type: 'hasMany', - order: i, - tnc: hm.tn, - handler_type: 1 - }); - - } + this.log( + "xcTableRename : Updating table name and route path in 'nc_routes' metatable - '%s' => '%s' (HasMany relation)", + oldTablename, + newTablename + ); + await this.xcMeta.metaUpdate( + this.projectId, + this.dbAlias, + 'nc_routes', + { + path: route.path, + tn: newTablename, + title: name + }, + { + title: oldName, + handler: JSON.stringify(route.handler), + tn: oldTablename, + type: route.type, + relation_type: 'hasMany', + order: i, + tnc: hm.tn, + handler_type: 1 + } + ); + }; }); - hmRoutesInsertion.push(async () => { - await this.xcMeta.metaUpdate(this.projectId, this.dbAlias, 'nc_routes', { - tn: newTablename, - title: name - }, { - tn: oldTablename, - title: oldName, - handler_type: 2 - }) + await this.xcMeta.metaUpdate( + this.projectId, + this.dbAlias, + 'nc_routes', + { + tn: newTablename, + title: name + }, + { + tn: oldTablename, + title: oldName, + handler_type: 2 + } + ); }); - - await NcHelp.executeOperations(hmRoutesInsertion, this.connectionConfig.client); - + await NcHelp.executeOperations( + hmRoutesInsertion, + this.connectionConfig.client + ); } /* handle belongs to routes and controllers */ for (const bt of meta.belongsTo) { - const rendererArgs1 = this.generateRendererArgs(this.generateContextForBelongsTo({ - ...ctx, - tn: bt.tn - }, bt.rtn)); - const btRoutes = new ExpressXcTsRoutesBt(rendererArgs1).getObjectWithoutFunctions(); + const rendererArgs1 = this.generateRendererArgs( + this.generateContextForBelongsTo( + { + ...ctx, + tn: bt.tn + }, + bt.rtn + ) + ); + const btRoutes = new ExpressXcTsRoutesBt( + rendererArgs1 + ).getObjectWithoutFunctions(); newSwagger.push(new SwaggerXcBt(rendererArgs1).getObject()); const oldName = `${oldTablename}Bt${bt.rtn}`; @@ -845,40 +1149,58 @@ export class RestApiBuilder extends BaseApiBuilder { const btRoutesInsertion = btRoutes.map((route, i) => { return async () => { + this.log( + "xcTableRename : Updating table name and route path in 'nc_routes' metatable - '%s' => '%s' (BelongsTo relation)", + oldTablename, + newTablename + ); + + await this.xcMeta.metaUpdate( + this.projectId, + this.dbAlias, + 'nc_routes', + { + path: route.path, + tn: newTablename, + title: name + }, + { + tn: oldTablename, + title: oldName, + order: i, + type: route.type, + handler: JSON.stringify(route.handler), + relation_type: 'belongsTo', + tnp: bt.rtn, + handler_type: 1 + } + ); + }; + }); - this.log('xcTableRename : Updating table name and route path in \'nc_routes\' metatable - \'%s\' => \'%s\' (BelongsTo relation)', oldTablename, newTablename) - - await this.xcMeta.metaUpdate(this.projectId, this.dbAlias, 'nc_routes', { - path: route.path, + btRoutesInsertion.push(async () => { + await this.xcMeta.metaUpdate( + this.projectId, + this.dbAlias, + 'nc_routes', + { tn: newTablename, - title: name, - }, { + title: name + }, + { tn: oldTablename, title: oldName, - order: i, - type: route.type, - handler: JSON.stringify(route.handler), + handler_type: 2, relation_type: 'belongsTo', - tnp: bt.rtn, - handler_type: 1 - }) - } - }); - - btRoutesInsertion.push(async () => { - await this.xcMeta.metaUpdate(this.projectId, this.dbAlias, 'nc_routes', { - tn: newTablename, - title: name - }, { - tn: oldTablename, - title: oldName, - handler_type: 2, - relation_type: 'belongsTo', - tnp: bt.rtn, - }) + tnp: bt.rtn + } + ); }); - await NcHelp.executeOperations(btRoutesInsertion, this.connectionConfig.client); + await NcHelp.executeOperations( + btRoutesInsertion, + this.connectionConfig.client + ); } }); @@ -890,36 +1212,64 @@ export class RestApiBuilder extends BaseApiBuilder { // reload routes and update meta for relation tables for (const relationTable of relatedTableList) { const newRelatedTableSwagger = []; - const rHasMany = this.extractHasManyRelationsOfTable(relations, relationTable); - const rBelongsTo = this.extractBelongsToRelationsOfTable(relations, relationTable); - const rCtx = this.generateContextForTable(relationTable, [], relations, rHasMany, rBelongsTo); - newRelatedTableSwagger.push(new SwaggerXc({ctx: rCtx}).getObject()); + const rHasMany = this.extractHasManyRelationsOfTable( + relations, + relationTable + ); + const rBelongsTo = this.extractBelongsToRelationsOfTable( + relations, + relationTable + ); + const rCtx = this.generateContextForTable( + relationTable, + [], + relations, + rHasMany, + rBelongsTo + ); + newRelatedTableSwagger.push(new SwaggerXc({ ctx: rCtx }).getObject()); if (oldTablename !== newTablename) { /* create models from table */ - const rMeta = ModelXcMetaFactory.create(this.connectionConfig, this.generateRendererArgs(rCtx)).getObject(); + const rMeta = ModelXcMetaFactory.create( + this.connectionConfig, + this.generateRendererArgs(rCtx) + ).getObject(); // update existing model meta with new details(relation tables) - const rExistingModel = await this.xcMeta.metaGet(this.projectId, this.dbAlias, 'nc_models', {'title': relationTable}); + const rExistingModel = await this.xcMeta.metaGet( + this.projectId, + this.dbAlias, + 'nc_models', + { title: relationTable } + ); if (rExistingModel) { - - - this.log('xcTableRename : Updating meta for relation table \'%s\' on behalf of table rename - \'%s\' => \'%s\' (BelongsTo relation)', relationTable, oldTablename, newTablename) - + this.log( + "xcTableRename : Updating meta for relation table '%s' on behalf of table rename - '%s' => '%s' (BelongsTo relation)", + relationTable, + oldTablename, + newTablename + ); // todo: persisting old table_alias and columnAlias const oldMeta = JSON.parse(rExistingModel.meta); Object.assign(oldMeta, { hasMany: rMeta.hasMany, - belongsTo: rMeta.belongsTo, + belongsTo: rMeta.belongsTo }); // await this.sqlClient.knex('nc_models').update({ // meta: JSON.stringify(oldMeta) // }).where('title', relationTable) - await this.xcMeta.metaUpdate(this.projectId, this.dbAlias, 'nc_models', { - meta: JSON.stringify(oldMeta) - }, {'title': relationTable}) + await this.xcMeta.metaUpdate( + this.projectId, + this.dbAlias, + 'nc_models', + { + meta: JSON.stringify(oldMeta) + }, + { title: relationTable } + ); this.metas[relationTable] = oldMeta; } @@ -928,83 +1278,141 @@ export class RestApiBuilder extends BaseApiBuilder { // update has many to routes for (const hmRelation of rHasMany) { - newRelatedTableSwagger.push(new SwaggerXcHm({ctx: this.generateContextForHasMany(rCtx, hmRelation.tn)}).getObject()); + newRelatedTableSwagger.push( + new SwaggerXcHm({ + ctx: this.generateContextForHasMany(rCtx, hmRelation.tn) + }).getObject() + ); if (hmRelation.tn === newTablename) { - - const oldHmRoutes: any[] = new ExpressXcTsRoutesHm(this.generateRendererArgs(this.generateContextForHasMany(rCtx, oldTablename))).getObjectWithoutFunctions(); - const hmRoutes: any[] = new ExpressXcTsRoutesHm(this.generateRendererArgs(this.generateContextForHasMany(rCtx, newTablename))).getObjectWithoutFunctions(); + const oldHmRoutes: any[] = new ExpressXcTsRoutesHm( + this.generateRendererArgs( + this.generateContextForHasMany(rCtx, oldTablename) + ) + ).getObjectWithoutFunctions(); + const hmRoutes: any[] = new ExpressXcTsRoutesHm( + this.generateRendererArgs( + this.generateContextForHasMany(rCtx, newTablename) + ) + ).getObjectWithoutFunctions(); for (const [i, newHmRoute] of Object.entries(hmRoutes)) { const oldHmRoute: any = oldHmRoutes[i]; - this.log(`xcTableRename : Updating routes for ${relationTable}Hm${newTablename} on behalf of '%table rename' - '%s' => '%s' (HasMany relation)`, relationTable, oldTablename, newTablename) - - - await this.xcMeta.metaUpdate(this.projectId, this.dbAlias, 'nc_routes', { - title: `${relationTable}Hm${newTablename}`, - path: newHmRoute.path, - tnc: newTablename - }, { - path: oldHmRoute.path, - type: oldHmRoute.type, tnc: oldTablename - }) + this.log( + `xcTableRename : Updating routes for ${relationTable}Hm${newTablename} on behalf of '%table rename' - '%s' => '%s' (HasMany relation)`, + relationTable, + oldTablename, + newTablename + ); + + await this.xcMeta.metaUpdate( + this.projectId, + this.dbAlias, + 'nc_routes', + { + title: `${relationTable}Hm${newTablename}`, + path: newHmRoute.path, + tnc: newTablename + }, + { + path: oldHmRoute.path, + type: oldHmRoute.type, + tnc: oldTablename + } + ); } - await this.xcMeta.metaUpdate(this.projectId, this.dbAlias, 'nc_routes', { - title: `${relationTable}Hm${newTablename}`, - }, { - title: `${relationTable}Hm${oldTablename}`, - }) + await this.xcMeta.metaUpdate( + this.projectId, + this.dbAlias, + 'nc_routes', + { + title: `${relationTable}Hm${newTablename}` + }, + { + title: `${relationTable}Hm${oldTablename}` + } + ); } } // update belongs to routes for (const btRelation of rBelongsTo) { - newRelatedTableSwagger.push(new SwaggerXcBt({ctx: this.generateContextForBelongsTo(rCtx, btRelation.rtn)}).getObject()); + newRelatedTableSwagger.push( + new SwaggerXcBt({ + ctx: this.generateContextForBelongsTo(rCtx, btRelation.rtn) + }).getObject() + ); if (btRelation.rtn === newTablename) { - const newBtRoutes: any[] = new ExpressXcTsRoutesBt(this.generateRendererArgs(this.generateContextForBelongsTo(rCtx, newTablename))).getObjectWithoutFunctions(); - const oldBtRoutes: any[] = new ExpressXcTsRoutesBt(this.generateRendererArgs(this.generateContextForBelongsTo(rCtx, oldTablename))).getObjectWithoutFunctions(); + const newBtRoutes: any[] = new ExpressXcTsRoutesBt( + this.generateRendererArgs( + this.generateContextForBelongsTo(rCtx, newTablename) + ) + ).getObjectWithoutFunctions(); + const oldBtRoutes: any[] = new ExpressXcTsRoutesBt( + this.generateRendererArgs( + this.generateContextForBelongsTo(rCtx, oldTablename) + ) + ).getObjectWithoutFunctions(); for (const [i, newBtRoute] of Object.entries(newBtRoutes)) { const oldBtRoute: any = oldBtRoutes[i]; - this.log(`xcTableRename : Updating routes for ${relationTable}Bt${oldTablename} on behalf of '%table rename' - '%s' => '%s' (BelongsTo relation)`, relationTable, oldTablename, newTablename) - await this.xcMeta.metaUpdate(this.projectId, this.dbAlias, 'nc_routes', { - title: `${relationTable}Bt${newTablename}`, - path: newBtRoute.path, - tnp: newTablename - }, { - title: `${relationTable}Bt${oldTablename}`, - type: oldBtRoute.type, - tnp: oldTablename - }) + this.log( + `xcTableRename : Updating routes for ${relationTable}Bt${oldTablename} on behalf of '%table rename' - '%s' => '%s' (BelongsTo relation)`, + relationTable, + oldTablename, + newTablename + ); + await this.xcMeta.metaUpdate( + this.projectId, + this.dbAlias, + 'nc_routes', + { + title: `${relationTable}Bt${newTablename}`, + path: newBtRoute.path, + tnp: newTablename + }, + { + title: `${relationTable}Bt${oldTablename}`, + type: oldBtRoute.type, + tnp: oldTablename + } + ); } - await this.xcMeta.metaUpdate(this.projectId, this.dbAlias, 'nc_routes', { - title: `${relationTable}Bt${newTablename}`, - }, { - title: `${relationTable}Bt${oldTablename}`, - }) + await this.xcMeta.metaUpdate( + this.projectId, + this.dbAlias, + 'nc_routes', + { + title: `${relationTable}Bt${newTablename}` + }, + { + title: `${relationTable}Bt${oldTablename}` + } + ); } } /* Reload relation tables : end */ - await this.mergeAndUpdateSwagger(relationTable as string, newRelatedTableSwagger); - + await this.mergeAndUpdateSwagger( + relationTable as string, + newRelatedTableSwagger + ); } // load routes and models from db - await this.xcTablesRead([...relatedTableList, newTablename]) + await this.xcTablesRead([...relatedTableList, newTablename]); await this.onSwaggerDocUpdate(newTablename); - } // NOTE: xc-meta public async onRelationCreate(tnp: string, tnc: string, args): Promise { - await super.onRelationCreate(tnp, tnc, args) + await super.onRelationCreate(tnp, tnc, args); // const newRelatedTableSwagger = []; - this.log('onRelationCreate : \'%s\' ==> \'%s\'', tnp, tnc) + this.log("onRelationCreate : '%s' ==> '%s'", tnp, tnc); - this.deleteRoutesForTables([tnp, tnc]) + this.deleteRoutesForTables([tnp, tnc]); const relations = await this.getXcRelationList(); { const swaggerArr = []; @@ -1016,58 +1424,90 @@ export class RestApiBuilder extends BaseApiBuilder { hasMany.forEach(r => { r._rtn = this.getTableNameAlias(r.rtn); r._tn = this.getTableNameAlias(r.tn); - }) - - const ctx = this.generateContextForTable(tnp, columns, relations, hasMany, belongsTo); - const meta = ModelXcMetaFactory.create(this.connectionConfig, {dir: '', ctx, filename: ''}).getObject(); + }); + const ctx = this.generateContextForTable( + tnp, + columns, + relations, + hasMany, + belongsTo + ); + const meta = ModelXcMetaFactory.create(this.connectionConfig, { + dir: '', + ctx, + filename: '' + }).getObject(); // update old model meta with new details - const existingModel = await this.xcMeta.metaGet(this.projectId, this.dbAlias, 'nc_models', {'title': tnp}); + const existingModel = await this.xcMeta.metaGet( + this.projectId, + this.dbAlias, + 'nc_models', + { title: tnp } + ); let queryParams; try { queryParams = JSON.parse(existingModel.query_params); - } catch (e) { /* */ + } catch (e) { + /* */ } - // swaggerArr.push(JSON.parse(existingModel.schema)); if (existingModel) { - this.log(`onRelationCreate : Updating model metadata for parent table '%s'`, tnp); + this.log( + `onRelationCreate : Updating model metadata for parent table '%s'`, + tnp + ); // todo: persisting old table_alias and columnAlias // todo: get enable state of other relations const oldMeta = JSON.parse(existingModel.meta); meta.hasMany.forEach(hm => { hm.enabled = true; - }) + }); Object.assign(oldMeta, { - hasMany: meta.hasMany, + hasMany: meta.hasMany }); - /* Add new has many relation to virtual columns */ oldMeta.v = oldMeta.v || []; oldMeta.v.push({ hm: meta.hasMany.find(hm => hm.rtn === tnp && hm.tn === tnc), - _cn: `${this.getTableNameAlias(tnp)} => ${this.getTableNameAlias(tnc)}` - }) - - swaggerArr.push(new SwaggerXc({ctx: {...ctx, v: oldMeta.v}}).getObject()); + _cn: `${this.getTableNameAlias(tnp)} => ${this.getTableNameAlias( + tnc + )}` + }); + swaggerArr.push( + new SwaggerXc({ ctx: { ...ctx, v: oldMeta.v } }).getObject() + ); if (queryParams?.showFields) { - queryParams.showFields[`${this.getTableNameAlias(tnp)} => ${this.getTableNameAlias(tnc)}`] = true; + queryParams.showFields[ + `${this.getTableNameAlias(tnp)} => ${this.getTableNameAlias(tnc)}` + ] = true; } - await this.xcMeta.metaUpdate(this.projectId, this.dbAlias, 'nc_models', { - meta: JSON.stringify(oldMeta), - ...(queryParams ? {query_params: JSON.stringify(queryParams)} : {}) - }, {'title': tnp}) - + await this.xcMeta.metaUpdate( + this.projectId, + this.dbAlias, + 'nc_models', + { + meta: JSON.stringify(oldMeta), + ...(queryParams + ? { query_params: JSON.stringify(queryParams) } + : {}) + }, + { title: tnp } + ); } - const rendererArgs = this.generateRendererArgs(this.generateContextForHasMany(ctx, tnc)); - const hmRoutes: any[] = new ExpressXcTsRoutesHm(rendererArgs).getObjectWithoutFunctions(); + const rendererArgs = this.generateRendererArgs( + this.generateContextForHasMany(ctx, tnc) + ); + const hmRoutes: any[] = new ExpressXcTsRoutesHm( + rendererArgs + ).getObjectWithoutFunctions(); swaggerArr.push(new SwaggerXcHm(rendererArgs).getObject()); const name = `${tnp}Hm${tnc}`; @@ -1075,154 +1515,230 @@ export class RestApiBuilder extends BaseApiBuilder { /* handle has many relational routes */ const hmRoutesInsertion = hmRoutes.map((route, i) => { return async () => { - await this.xcMeta.metaInsert(this.projectId, this.dbAlias, 'nc_routes', { - title: name, - tn: tnp, - order: i, - path: route.path, - type: route.type, - handler: JSON.stringify(route.handler), - acl: JSON.stringify(route.acl), - relation_type: 'hasMany', - tnc: tnc - }) - } + await this.xcMeta.metaInsert( + this.projectId, + this.dbAlias, + 'nc_routes', + { + title: name, + tn: tnp, + order: i, + path: route.path, + type: route.type, + handler: JSON.stringify(route.handler), + acl: JSON.stringify(route.acl), + relation_type: 'hasMany', + tnc: tnc + } + ); + }; }); hmRoutesInsertion.push(async () => { - await this.xcMeta.metaInsert(this.projectId, this.dbAlias, 'nc_routes', { - tn: tnp, - title: name, - handler_type: 2, - relation_type: 'hasMany', - tnc: tnc - }) + await this.xcMeta.metaInsert( + this.projectId, + this.dbAlias, + 'nc_routes', + { + tn: tnp, + title: name, + handler_type: 2, + relation_type: 'hasMany', + tnc: tnc + } + ); }); - this.log(`onRelationCreate : Creating and inserting routes metadata of %s relation`, name); - await NcHelp.executeOperations(hmRoutesInsertion, this.connectionConfig.client); + this.log( + `onRelationCreate : Creating and inserting routes metadata of %s relation`, + name + ); + await NcHelp.executeOperations( + hmRoutesInsertion, + this.connectionConfig.client + ); await this.mergeAndUpdateSwagger(tnp, swaggerArr); - } { const swaggerArr = []; const columns = this.metas[tnc]?.columns; const belongsTo = this.extractBelongsToRelationsOfTable(relations, tnc); const hasMany = this.extractHasManyRelationsOfTable(relations, tnc); - const ctx = this.generateContextForTable(tnc, columns, relations, hasMany, belongsTo); - const meta = ModelXcMetaFactory.create(this.connectionConfig, this.generateRendererArgs(ctx)).getObject(); - + const ctx = this.generateContextForTable( + tnc, + columns, + relations, + hasMany, + belongsTo + ); + const meta = ModelXcMetaFactory.create( + this.connectionConfig, + this.generateRendererArgs(ctx) + ).getObject(); // set table name alias belongsTo.forEach(r => { r._rtn = this.getTableNameAlias(r.rtn); r._tn = this.getTableNameAlias(r.tn); - }) - + }); // update old model meta with new details - const existingModel = await this.xcMeta.metaGet(this.projectId, this.dbAlias, 'nc_models', {'title': tnc}); + const existingModel = await this.xcMeta.metaGet( + this.projectId, + this.dbAlias, + 'nc_models', + { title: tnc } + ); let queryParams; try { queryParams = JSON.parse(existingModel.query_params); - } catch (e) { /* */ + } catch (e) { + /* */ } - // swaggerArr.push(JSON.parse(existingModel.schema)) if (existingModel) { meta.belongsTo.forEach(hm => { hm.enabled = true; - }) - this.log(`onRelationCreate : Updating model metadata for child table '%s'`, tnc); + }); + this.log( + `onRelationCreate : Updating model metadata for child table '%s'`, + tnc + ); // todo: persisting old table_alias and columnAlias const oldMeta = JSON.parse(existingModel.meta); Object.assign(oldMeta, { - belongsTo: meta.belongsTo, + belongsTo: meta.belongsTo }); /* Add new belongs to relation to virtual columns */ oldMeta.v = oldMeta.v || []; oldMeta.v.push({ bt: meta.belongsTo.find(hm => hm.rtn === tnp && hm.tn === tnc), - _cn: `${this.getTableNameAlias(tnp)} <= ${this.getTableNameAlias(tnc)}` - }) - - swaggerArr.push(new SwaggerXc({ctx: {...ctx, v: oldMeta.v}}).getObject()); + _cn: `${this.getTableNameAlias(tnp)} <= ${this.getTableNameAlias( + tnc + )}` + }); + swaggerArr.push( + new SwaggerXc({ ctx: { ...ctx, v: oldMeta.v } }).getObject() + ); if (queryParams?.showFields) { - queryParams.showFields[`${this.getTableNameAlias(tnp)} <= ${this.getTableNameAlias(tnc)}`] = true; + queryParams.showFields[ + `${this.getTableNameAlias(tnp)} <= ${this.getTableNameAlias(tnc)}` + ] = true; } - await this.xcMeta.metaUpdate(this.projectId, this.dbAlias, 'nc_models', { - meta: JSON.stringify(oldMeta), - ...(queryParams ? {query_params: JSON.stringify(queryParams)} : {}) - }, {'title': tnc}) + await this.xcMeta.metaUpdate( + this.projectId, + this.dbAlias, + 'nc_models', + { + meta: JSON.stringify(oldMeta), + ...(queryParams + ? { query_params: JSON.stringify(queryParams) } + : {}) + }, + { title: tnc } + ); } - - const rendererArgs = this.generateRendererArgs(this.generateContextForBelongsTo(ctx, tnp)); - const btRoutes = new ExpressXcTsRoutesBt(rendererArgs).getObjectWithoutFunctions(); + const rendererArgs = this.generateRendererArgs( + this.generateContextForBelongsTo(ctx, tnp) + ); + const btRoutes = new ExpressXcTsRoutesBt( + rendererArgs + ).getObjectWithoutFunctions(); swaggerArr.push(new SwaggerXcBt(rendererArgs).getObject()); const name = `${tnc}Bt${tnp}`; const btRoutesInsertion = btRoutes.map((route, i) => { return async () => { - await this.xcMeta.metaInsert(this.projectId, this.dbAlias, 'nc_routes', { + await this.xcMeta.metaInsert( + this.projectId, + this.dbAlias, + 'nc_routes', + { + tn: tnc, + title: name, + order: i, + path: route.path, + type: route.type, + handler: JSON.stringify(route.handler), + acl: JSON.stringify(route.acl), + relation_type: 'belongsTo', + tnp: tnp + } + ); + }; + }); + btRoutesInsertion.push(async () => { + await this.xcMeta.metaInsert( + this.projectId, + this.dbAlias, + 'nc_routes', + { tn: tnc, title: name, - order: i, - path: route.path, - type: route.type, - handler: JSON.stringify(route.handler), - acl: JSON.stringify(route.acl), + handler_type: 2, relation_type: 'belongsTo', tnp: tnp - }) - } - }); - btRoutesInsertion.push(async () => { - await this.xcMeta.metaInsert(this.projectId, this.dbAlias, 'nc_routes', { - tn: tnc, - title: name, - handler_type: 2, - relation_type: 'belongsTo', - tnp: tnp - }) + } + ); }); - this.log(`onRelationCreate : Creating and inserting routes metadata of %s relation`, name); - await NcHelp.executeOperations(btRoutesInsertion, this.connectionConfig.client); + this.log( + `onRelationCreate : Creating and inserting routes metadata of %s relation`, + name + ); + await NcHelp.executeOperations( + btRoutesInsertion, + this.connectionConfig.client + ); await this.mergeAndUpdateSwagger(tnc, swaggerArr); - } await this.xcTablesRead([tnp, tnc]); await this.onSwaggerDocUpdate(tnp); - } - public async onRelationDelete(tnp: string, tnc: string, args: any): Promise { + public async onRelationDelete( + tnp: string, + tnc: string, + args: any + ): Promise { await super.onRelationDelete(tnp, tnc, args); - this.log('Within relation delete event - \'%s\' ==> \'%s\'', tnp, tnc) - this.deleteRoutesForTables([tnp, tnc]) + this.log("Within relation delete event - '%s' ==> '%s'", tnp, tnc); + this.deleteRoutesForTables([tnp, tnc]); const relations = await this.getXcRelationList(); { const hasMany = this.extractHasManyRelationsOfTable(relations, tnp); const ctx = this.generateContextForTable(tnp, [], relations, hasMany, []); - const meta = ModelXcMetaFactory.create(this.connectionConfig, {dir: '', ctx, filename: ''}).getObject(); + const meta = ModelXcMetaFactory.create(this.connectionConfig, { + dir: '', + ctx, + filename: '' + }).getObject(); // update old model meta with new details - const existingModel = await this.xcMeta.metaGet(this.projectId, this.dbAlias, 'nc_models', {'title': tnp}); + const existingModel = await this.xcMeta.metaGet( + this.projectId, + this.dbAlias, + 'nc_models', + { title: tnp } + ); const tagName = `${tnp}HasMany${tnc}`; - const swaggerObj = this.deleteTagFromSwaggerObj(existingModel.schema, tagName); + const swaggerObj = this.deleteTagFromSwaggerObj( + existingModel.schema, + tagName + ); if (existingModel) { this.log(`Updating model metadata for parent table '%s'`, tnp); @@ -1231,35 +1747,64 @@ export class RestApiBuilder extends BaseApiBuilder { const oldMeta = JSON.parse(existingModel.meta); Object.assign(oldMeta, { hasMany: meta.hasMany, - v: oldMeta.v.filter(({hm, lk}) => (!hm || hm.rtn !== tnp || hm.tn !== tnc) && - !(lk && lk.type === 'hm' && lk.rtn === tnp && lk.tn === tnc)) + v: oldMeta.v.filter( + ({ hm, lk }) => + (!hm || hm.rtn !== tnp || hm.tn !== tnc) && + !(lk && lk.type === 'hm' && lk.rtn === tnp && lk.tn === tnc) + ) }); // todo: delete from query_params - await this.xcMeta.metaUpdate(this.projectId, this.dbAlias, 'nc_models', { - title: tnp, - meta: JSON.stringify(oldMeta), - schema: JSON.stringify(swaggerObj) - }, {'title': tnp}) - + await this.xcMeta.metaUpdate( + this.projectId, + this.dbAlias, + 'nc_models', + { + title: tnp, + meta: JSON.stringify(oldMeta), + schema: JSON.stringify(swaggerObj) + }, + { title: tnp } + ); } const name = `${tnp}Hm${tnc}`; - - this.log(`Deleting routes metadata of %s relation`, name); /* handle has many relational routes */ - await this.xcMeta.metaDelete(this.projectId, this.dbAlias, 'nc_routes', {'title': name}); + this.log( + `Deleting routes metadata of %s relation`, + name + ); /* handle has many relational routes */ + await this.xcMeta.metaDelete(this.projectId, this.dbAlias, 'nc_routes', { + title: name + }); } { const belongsTo = this.extractBelongsToRelationsOfTable(relations, tnc); - const ctx = this.generateContextForTable(tnc, [], relations, [], belongsTo); - const meta = ModelXcMetaFactory.create(this.connectionConfig, this.generateRendererArgs(ctx)).getObject(); + const ctx = this.generateContextForTable( + tnc, + [], + relations, + [], + belongsTo + ); + const meta = ModelXcMetaFactory.create( + this.connectionConfig, + this.generateRendererArgs(ctx) + ).getObject(); // update old model meta with new details - const existingModel = await this.xcMeta.metaGet(this.projectId, this.dbAlias, 'nc_models', {'title': tnc}); + const existingModel = await this.xcMeta.metaGet( + this.projectId, + this.dbAlias, + 'nc_models', + { title: tnc } + ); const tagName = `${tnc}BelongsTo${tnp}`; - const swaggerObj = this.deleteTagFromSwaggerObj(existingModel.schema, tagName); + const swaggerObj = this.deleteTagFromSwaggerObj( + existingModel.schema, + tagName + ); if (existingModel) { this.log(`Updating model metadata for child table '%s'`, tnc); @@ -1268,25 +1813,34 @@ export class RestApiBuilder extends BaseApiBuilder { Object.assign(oldMeta, { belongsTo: meta.belongsTo, - v: oldMeta.v.filter(({bt, lk}) => (!bt || bt.rtn !== tnp || bt.tn !== tnc) && - !(lk && lk.type === 'bt' && lk.rtn === tnp && lk.tn === tnc)) + v: oldMeta.v.filter( + ({ bt, lk }) => + (!bt || bt.rtn !== tnp || bt.tn !== tnc) && + !(lk && lk.type === 'bt' && lk.rtn === tnp && lk.tn === tnc) + ) }); // todo: delete from query_params - await this.xcMeta.metaUpdate(this.projectId, + await this.xcMeta.metaUpdate( + this.projectId, this.dbAlias, - 'nc_models', { + 'nc_models', + { title: tnc, meta: JSON.stringify(oldMeta), schema: JSON.stringify(swaggerObj) - }, {'title': tnc}) + }, + { title: tnc } + ); } const name = `${tnc}Bt${tnp}`; this.log(`Deleting routes metadata of %s relation`, name); - await this.xcMeta.metaDelete(this.projectId, this.dbAlias, 'nc_routes', {'title': name}); + await this.xcMeta.metaDelete(this.projectId, this.dbAlias, 'nc_routes', { + title: name + }); } - await this.xcTablesRead([tnp, tnc]) + await this.xcTablesRead([tnp, tnc]); await this.onSwaggerDocUpdate(tnp); } @@ -1296,39 +1850,44 @@ export class RestApiBuilder extends BaseApiBuilder { await this.xcTablesRead([tn]); } - // NOTE: xc-meta public async onMiddlewareCodeUpdate(tn: string): Promise { this.log(`onMiddlewareCodeUpdate : %s`, tn); - const routes = await this.xcMeta.metaList(this.projectId, this.dbAlias, 'nc_routes', { - condition: { - tn: tn, - handler_type: 2 + const routes = await this.xcMeta.metaList( + this.projectId, + this.dbAlias, + 'nc_routes', + { + condition: { + tn: tn, + handler_type: 2 + } } - }); + ); const router = this.routers[tn]; - router.stack.splice(0, router.stack.length) + router.stack.splice(0, router.stack.length); for (const route of routes) { let middlewareBody = null; if (route.functions) { try { middlewareBody = JSON.parse(route.functions)[0]; } catch (e) { - console.log(e.message) + console.log(e.message); } } - this.log(`onMiddlewareCodeUpdate : Reloading routes with new middleware body for %s table`, tn); + this.log( + `onMiddlewareCodeUpdate : Reloading routes with new middleware body for %s table`, + tn + ); this.controllers[route.title] .updateMiddleware(middlewareBody) .remapRouters(router); } } - public async onToggleModels(enabledModels: string[]): Promise { - this.log(`onToggleModels : %o`, enabledModels); for (const handler of Object.values(this.routers)) { const index = this.router.stack.findIndex(r => r.handle === handler); @@ -1340,15 +1899,20 @@ export class RestApiBuilder extends BaseApiBuilder { } public async onToggleModelRelation(relationInModels: any): Promise { - this.log(`onToggleModelRelation :`) + this.log(`onToggleModelRelation :`); // filter out model names which toggled - const modelNames: string[] = [...new Set(relationInModels.map(rel => { - return rel.relationType === 'hm' ? rel.rtn : rel.tn - }))] as string[]; + const modelNames: string[] = [ + ...new Set( + relationInModels.map(rel => { + return rel.relationType === 'hm' ? rel.rtn : rel.tn; + }) + ) + ] as string[]; for (const modelName of modelNames) { - - const index = this.router.stack.findIndex(r => r.handle === this.routers[modelName]); + const index = this.router.stack.findIndex( + r => r.handle === this.routers[modelName] + ); if (index > -1) { this.router.stack.splice(index, 1); } @@ -1357,32 +1921,42 @@ export class RestApiBuilder extends BaseApiBuilder { } public async onViewCreate(viewName: string, args?: any): Promise { - this.log(`onViewCreate : '%s'`, viewName) - await this.xcTablesPopulate({tableNames: [{tn: viewName, _tn: args._tn}], type: 'view'}); + this.log(`onViewCreate : '%s'`, viewName); + await this.xcTablesPopulate({ + tableNames: [{ tn: viewName, _tn: args._tn }], + type: 'view' + }); } - public async onFunctionCreate(functionName: string): Promise { this.log(`onFunctionCreate : '%s'`, functionName); const functions = (await this.sqlClient.functionList())?.data?.list; - this.routers.___procedure.stack.splice(0, this.routers.___procedure.stack.length); + this.routers.___procedure.stack.splice( + 0, + this.routers.___procedure.stack.length + ); // do insertion parallelly - const functionObj = functions.find(f => f.function_name === functionName) + const functionObj = functions.find(f => f.function_name === functionName); if (functionObj) { - this.log(`onFunctionCreate : Generating and inserting '%s' function meta and acl`, functionName); + this.log( + `onFunctionCreate : Generating and inserting '%s' function meta and acl`, + functionName + ); await this.generateAndSaveAcl(functionObj.function_name, 'function'); await this.xcMeta.metaInsert(this.projectId, this.dbAlias, 'nc_models', { title: functionObj.function_name, - meta: JSON.stringify({...functionObj, type: 'function'}), + meta: JSON.stringify({ ...functionObj, type: 'function' }), type: 'function' - }) + }); } this.generateAndSaveAcl(functionName, 'function'); this.log(`onFunctionCreate : Update function and procedure routes`); this.procedureCtrl.functionsSet(functions); this.procedureCtrl.mapRoutes(this.routers.___procedure, this.customRoutes); - this.swaggerUpdate({addApis: {paths: this.procedureCtrl.getSwaggerObj().paths}}) + this.swaggerUpdate({ + addApis: { paths: this.procedureCtrl.getSwaggerObj().paths } + }); } // NOTE: xc-meta @@ -1394,34 +1968,50 @@ export class RestApiBuilder extends BaseApiBuilder { type: 'function' }); this.log(`onFunctionDelete : Update function and procedure routes`); - this.procedureCtrl.functionDelete(functionName) - this.routers.___procedure.stack.splice(0, this.routers.___procedure.stack.length); + this.procedureCtrl.functionDelete(functionName); + this.routers.___procedure.stack.splice( + 0, + this.routers.___procedure.stack.length + ); this.procedureCtrl.mapRoutes(this.routers.___procedure, this.customRoutes); - this.swaggerUpdate({addApis: this.procedureCtrl.getSwaggerObj(), deleteTags: ['Procedures', 'Functions']}) + this.swaggerUpdate({ + addApis: this.procedureCtrl.getSwaggerObj(), + deleteTags: ['Procedures', 'Functions'] + }); } // NOTE: xc-meta public async onProcedureCreate(procedureName: string): Promise { this.log(`onProcedureCreate : '%s'`, procedureName); const procedures = (await this.sqlClient.procedureList())?.data?.list; - this.routers.___procedure.stack.splice(0, this.routers.___procedure.stack.length); + this.routers.___procedure.stack.splice( + 0, + this.routers.___procedure.stack.length + ); // do insertion parallelly - const procedureObj = procedures.find(f => f.procedure_name === procedureName) + const procedureObj = procedures.find( + f => f.procedure_name === procedureName + ); if (procedureObj) { - this.log(`onProcedureCreate : Generating and inserting '%s' procedure meta and acl`, procedureName); + this.log( + `onProcedureCreate : Generating and inserting '%s' procedure meta and acl`, + procedureName + ); await this.generateAndSaveAcl(procedureObj.procedure_name, 'procedure'); await this.xcMeta.metaInsert(this.projectId, this.dbAlias, 'nc_models', { title: procedureObj.procedure_name, - meta: JSON.stringify({...procedureObj, type: 'procedure'}), + meta: JSON.stringify({ ...procedureObj, type: 'procedure' }), type: 'procedure' - }) + }); } this.generateAndSaveAcl(procedureName, 'procedure'); this.log(`onProcedureCreate : Update function and procedure routes`); this.procedureCtrl.proceduresSet(procedures); this.procedureCtrl.mapRoutes(this.routers.___procedure, this.customRoutes); - this.swaggerUpdate({addApis: {paths: this.procedureCtrl.getSwaggerObj().paths}}) + this.swaggerUpdate({ + addApis: { paths: this.procedureCtrl.getSwaggerObj().paths } + }); } // NOTE: xc-meta @@ -1434,22 +2024,31 @@ export class RestApiBuilder extends BaseApiBuilder { }); this.log(`onProcedureDelete : Update function and procedure routes`); this.procedureCtrl.procedureDelete(procedureName); - this.routers.___procedure.stack.splice(0, this.routers.___procedure.stack.length); + this.routers.___procedure.stack.splice( + 0, + this.routers.___procedure.stack.length + ); this.procedureCtrl.mapRoutes(this.routers.___procedure, this.customRoutes); - this.swaggerUpdate({addApis: this.procedureCtrl.getSwaggerObj(), deleteTags: ['Procedures', 'Functions']}) + this.swaggerUpdate({ + addApis: this.procedureCtrl.getSwaggerObj(), + deleteTags: ['Procedures', 'Functions'] + }); } public async onSwaggerDocUpdate(tn: any): Promise { this.log(`onSwaggerDocUpdate : '%s'`, tn); - const swaggerDocs = (await this.xcMeta.metaList(this.projectId, this.dbAlias, 'nc_models')) - .filter(m => m.schema).map(m => JSON.parse(m.schema)); + const swaggerDocs = ( + await this.xcMeta.metaList(this.projectId, this.dbAlias, 'nc_models') + ) + .filter(m => m.schema) + .map(m => JSON.parse(m.schema)); swaggerDocs.push(this.procedureCtrl.getSwaggerObj()); const swaggerDoc = { tags: [], paths: {}, definitions: {} - } + }; swaggerDocs.forEach(swaggerJson => { swaggerDoc.tags.push(...(swaggerJson.tags || [])); @@ -1463,13 +2062,22 @@ export class RestApiBuilder extends BaseApiBuilder { public async onTableUpdate(changeObj: any): Promise { this.log(`onTableUpdate : '%s'`, changeObj.tn); - await super.onTableUpdate(changeObj, async ({ctx}) => { + await super.onTableUpdate(changeObj, async ({ ctx }) => { // todo: take backup - const swaggerDoc = await new SwaggerXc({dir: '', ctx, filename: ''}).getObject(); - const meta = await this.xcMeta.metaGet(this.projectId, this.dbAlias, 'nc_models', { - title: changeObj.tn, - type: 'table' - }); + const swaggerDoc = await new SwaggerXc({ + dir: '', + ctx, + filename: '' + }).getObject(); + const meta = await this.xcMeta.metaGet( + this.projectId, + this.dbAlias, + 'nc_models', + { + title: changeObj.tn, + type: 'table' + } + ); const oldSwaggerDoc = JSON.parse(meta.schema); // // keep upto 5 schema backup on table update @@ -1478,31 +2086,33 @@ export class RestApiBuilder extends BaseApiBuilder { // previousSchemas = [...JSON.parse(meta.schema_previous), oldSwaggerDoc].slice(-5); // } - oldSwaggerDoc.definitions = swaggerDoc.definitions; - await this.xcMeta.metaUpdate(this.projectId, this.dbAlias, 'nc_models', { - schema: JSON.stringify(oldSwaggerDoc), - // schema_previous: JSON.stringify(previousSchemas) - }, { - title: changeObj.tn, - type: 'table' - }); + await this.xcMeta.metaUpdate( + this.projectId, + this.dbAlias, + 'nc_models', + { + schema: JSON.stringify(oldSwaggerDoc) + // schema_previous: JSON.stringify(previousSchemas) + }, + { + title: changeObj.tn, + type: 'table' + } + ); await this.onSwaggerDocUpdate(changeObj.tn); }); - - } // NOTE: xc-meta // @ts-ignore private async populteProcedureAndFunctionRoutes(_args?: { - functions?: string[], - procedures?: string[], + functions?: string[]; + procedures?: string[]; }) { - this.log('populteProcedureAndFunctionRoutes'); - const router = this.routers.___procedure = Router(); + const router = (this.routers.___procedure = Router()); const functions = []; const procedures = []; @@ -1519,102 +2129,166 @@ export class RestApiBuilder extends BaseApiBuilder { // this.apiCount += this.proceduresCount; // } catch (e) { // } - this.procedureCtrl = new RestCtrlProcedure(this, functions, procedures, this.procedureOrFunctionAcls); + this.procedureCtrl = new RestCtrlProcedure( + this, + functions, + procedures, + this.procedureOrFunctionAcls + ); this.procedureCtrl.mapRoutes(router, this.customRoutes); this.router.use(`/api/${this.routeVersionLetter}`, router); // do insertion parallelly if (functions) { for (const functionObj of functions) { - - this.log('populteProcedureAndFunctionRoutes : Inserting metadata and acl for \'%s\' - function', functionObj.function_name); + this.log( + "populteProcedureAndFunctionRoutes : Inserting metadata and acl for '%s' - function", + functionObj.function_name + ); await this.generateAndSaveAcl(functionObj.function_name, 'function'); - await this.xcMeta.metaInsert(this.projectId, this.dbAlias, 'nc_models', { - title: functionObj.function_name, - meta: JSON.stringify({...functionObj, type: 'function'}), - type: 'function' - }) + await this.xcMeta.metaInsert( + this.projectId, + this.dbAlias, + 'nc_models', + { + title: functionObj.function_name, + meta: JSON.stringify({ ...functionObj, type: 'function' }), + type: 'function' + } + ); } } if (procedures) { for (const procedureObj of procedures) { - this.log('populteProcedureAndFunctionRoutes : Inserting metadata and acl for \'%s\' - procedure', procedureObj.procedure_name); + this.log( + "populteProcedureAndFunctionRoutes : Inserting metadata and acl for '%s' - procedure", + procedureObj.procedure_name + ); await this.generateAndSaveAcl(procedureObj.procedure_name, 'procedure'); - await this.xcMeta.metaInsert(this.projectId, this.dbAlias, 'nc_models', { - title: procedureObj.procedure_name, - meta: JSON.stringify({...procedureObj, type: 'procedure'}), - type: 'procedure' - }) + await this.xcMeta.metaInsert( + this.projectId, + this.dbAlias, + 'nc_models', + { + title: procedureObj.procedure_name, + meta: JSON.stringify({ ...procedureObj, type: 'procedure' }), + type: 'procedure' + } + ); } } } private async generateSwaggerJson(swaggerDoc) { if (!this.config.try) { - this.log('generateSwaggerJson : Generating swagger.json') - const swaggerFilePath = path.join(this.config.toolDir, 'nc', this.projectId, this.getDbAlias(), 'swagger'); + this.log('generateSwaggerJson : Generating swagger.json'); + const swaggerFilePath = path.join( + this.config.toolDir, + 'nc', + this.projectId, + this.getDbAlias(), + 'swagger' + ); mkdirp.sync(swaggerFilePath); - fs.writeFileSync(path.join(swaggerFilePath, 'swagger.json'), JSON.stringify(swaggerDoc)); + fs.writeFileSync( + path.join(swaggerFilePath, 'swagger.json'), + JSON.stringify(swaggerDoc) + ); } this.router.get(`/${this.getDbAlias()}/swagger`, async (_req, res) => { - res.send(ejs.render((await import('./ui/auth/swagger')).default, { - ncPublicUrl: process.env.NC_PUBLIC_URL || '', - baseUrl: `/`, - dbAlias: this.getDbAlias(), - projectId: this.projectId - })); + res.send( + ejs.render((await import('./ui/auth/swagger')).default, { + ncPublicUrl: process.env.NC_PUBLIC_URL || '', + baseUrl: `/`, + dbAlias: this.getDbAlias(), + projectId: this.projectId + }) + ); }); this.router.get(`/${this.getDbAlias()}/swagger.json`, async (req, res) => { // todo: optimize - let swaggerBaseDocument: any = JSON.parse(JSON.stringify(await import('./ui/auth/swagger-base.xc.json'))); + let swaggerBaseDocument: any = JSON.parse( + JSON.stringify(await import('./ui/auth/swagger-base.xc.json')) + ); - if (this.config?.auth?.jwt?.dbAlias !== this.connectionConfig.meta.dbAlias) { - swaggerBaseDocument = {...swaggerBaseDocument, tags: [], definitions: {}, paths: {}}; + if ( + this.config?.auth?.jwt?.dbAlias !== this.connectionConfig.meta.dbAlias + ) { + swaggerBaseDocument = { + ...swaggerBaseDocument, + tags: [], + definitions: {}, + paths: {} + }; } - const host = process.env.NC_PUBLIC_URL ? new URL(process.env.NC_PUBLIC_URL)?.host : req.get('host'); - const scheme = process.env.NC_PUBLIC_URL ? new URL(process.env.NC_PUBLIC_URL)?.protocol.slice(0, -1) : req.protocol; + const host = process.env.NC_PUBLIC_URL + ? new URL(process.env.NC_PUBLIC_URL)?.host + : req.get('host'); + const scheme = process.env.NC_PUBLIC_URL + ? new URL(process.env.NC_PUBLIC_URL)?.protocol.slice(0, -1) + : req.protocol; swaggerBaseDocument.host = host; - swaggerBaseDocument.schemes = [scheme, scheme === 'http' ? 'https' : 'http']; - glob.sync(path.join(this.config.toolDir, 'nc', this.projectId, this.getDbAlias(), 'swagger', 'swagger.json')).forEach(jsonFile => { - const swaggerJson = JSON.parse(fs.readFileSync(jsonFile, 'utf8')); - swaggerBaseDocument.tags.push(...swaggerJson.tags); - Object.assign(swaggerBaseDocument.paths, swaggerJson.paths); - Object.assign(swaggerBaseDocument.definitions, swaggerJson.definitions); - }); - res.json(swaggerBaseDocument) + swaggerBaseDocument.schemes = [ + scheme, + scheme === 'http' ? 'https' : 'http' + ]; + glob + .sync( + path.join( + this.config.toolDir, + 'nc', + this.projectId, + this.getDbAlias(), + 'swagger', + 'swagger.json' + ) + ) + .forEach(jsonFile => { + const swaggerJson = JSON.parse(fs.readFileSync(jsonFile, 'utf8')); + swaggerBaseDocument.tags.push(...swaggerJson.tags); + Object.assign(swaggerBaseDocument.paths, swaggerJson.paths); + Object.assign( + swaggerBaseDocument.definitions, + swaggerJson.definitions + ); + }); + res.json(swaggerBaseDocument); }); - - } - private deleteTagFromSwaggerObj(swaggerObjOrString: any | string, tagName: string): any { + private deleteTagFromSwaggerObj( + swaggerObjOrString: any | string, + tagName: string + ): any { let swaggerObj = swaggerObjOrString; if (typeof swaggerObj === 'string') { swaggerObj = JSON.parse(swaggerObj); } swaggerObj.tags = swaggerObj.tags.filter(t => t.name !== tagName); for (const routePath of Object.keys(swaggerObj.paths)) { - if ((Object.values(swaggerObj.paths[routePath])[0] as any).tags.includes(tagName)) { + if ( + (Object.values(swaggerObj.paths[routePath])[0] as any).tags.includes( + tagName + ) + ) { delete swaggerObj.paths[routePath]; } } return swaggerObj; } - private deleteRoutesForTables(tableNames: any[]): void { - this.log(`deleteRoutesForTables : %o`, tableNames) + this.log(`deleteRoutesForTables : %o`, tableNames); // delete routes and models for table and it's relation tables for (const table of tableNames) { - const ctrlIndex = this.router.stack.findIndex(r => { return r.handle === this.routers[table]; - }) + }); if (ctrlIndex > -1) { - this.router.stack.splice(ctrlIndex, 1) + this.router.stack.splice(ctrlIndex, 1); } delete this.routers[table]; @@ -1623,42 +2297,53 @@ export class RestApiBuilder extends BaseApiBuilder { // @ts-ignore private swaggerUpdate(args: { - deleteApis?: any, - addApis?: any, - deleteTags?: string[] + deleteApis?: any; + addApis?: any; + deleteTags?: string[]; }) { - - this.log(`swaggerUpdate :`,); + this.log(`swaggerUpdate :`); if (!args.deleteApis && !args.addApis && !args.deleteTags) { return; } /* load swagger JSON */ - const swaggerFilePath = path.join(this.config.toolDir, 'nc', this.projectId, this.getDbAlias(), 'swagger'); - const swaggerJson = JSON.parse(fs.readFileSync(path.join(swaggerFilePath, 'swagger.json'), 'utf8')); + const swaggerFilePath = path.join( + this.config.toolDir, + 'nc', + this.projectId, + this.getDbAlias(), + 'swagger' + ); + const swaggerJson = JSON.parse( + fs.readFileSync(path.join(swaggerFilePath, 'swagger.json'), 'utf8') + ); /* remove tags, paths and keys */ if (args.deleteApis) { this.log(`swaggerUpdate : deleting swagger apis`); - const {tags, paths, definitions} = args.deleteApis; + const { tags, paths, definitions } = args.deleteApis; if (tags) { - swaggerJson.tags = swaggerJson.tags.filter(({name}) => tags.find(t => t.name === name)) + swaggerJson.tags = swaggerJson.tags.filter(({ name }) => + tags.find(t => t.name === name) + ); } if (paths) { Object.keys(paths).forEach(path => delete swaggerJson.tags[path]); } if (definitions) { - Object.keys(definitions).forEach(def => delete swaggerJson.definitions[def]); + Object.keys(definitions).forEach( + def => delete swaggerJson.definitions[def] + ); } } /* add tags, paths and defnitions */ if (args.addApis) { this.log(`swaggerUpdate : adding swagger apis`); - const {tags, paths, definitions} = args.addApis; + const { tags, paths, definitions } = args.addApis; if (tags) { swaggerJson.tags.push(...tags); } @@ -1674,29 +2359,33 @@ export class RestApiBuilder extends BaseApiBuilder { if (args.deleteTags) { this.log(`swaggerUpdate : deleting swagger tags : %o`, args.deleteTags); for (const tag of args.deleteTags) { - swaggerJson.tags = swaggerJson.tags.filter(t => t.name !== tag); Object.keys(swaggerJson.paths).forEach(p => { - if (swaggerJson.paths?.[p]?.[Object.keys(swaggerJson.paths[p])[0]]?.tags?.includes(tag)) { + if ( + swaggerJson.paths?.[p]?.[ + Object.keys(swaggerJson.paths[p])[0] + ]?.tags?.includes(tag) + ) { delete swaggerJson.paths[p]; } - }) + }); if (swaggerJson.definitions) { - if(swaggerJson.definitions[tag]) { + if (swaggerJson.definitions[tag]) { delete swaggerJson.definitions[tag]; } - if(swaggerJson.definitions[`${tag}Nested`]) { + if (swaggerJson.definitions[`${tag}Nested`]) { delete swaggerJson.definitions[`${tag}Nested`]; } } } } - - fs.writeFileSync(path.join(swaggerFilePath, 'swagger.json'), JSON.stringify(swaggerJson)); - + fs.writeFileSync( + path.join(swaggerFilePath, 'swagger.json'), + JSON.stringify(swaggerJson) + ); } private log(str, ...args) { @@ -1709,72 +2398,114 @@ export class RestApiBuilder extends BaseApiBuilder { tags: [], paths: {}, definitions: {} - } + }; - this.log(`mergeAndUpdateSwagger : Merging all the swagger docs object of '%s' table`, tn); + this.log( + `mergeAndUpdateSwagger : Merging all the swagger docs object of '%s' table`, + tn + ); swaggerDefs.forEach(swaggerJson => { swaggerDoc.tags.push(...swaggerJson.tags); Object.assign(swaggerDoc.paths, swaggerJson.paths); Object.assign(swaggerDoc.definitions, swaggerJson.definitions); }); - this.log(`mergeAndUpdateSwagger : Saving swagger JSON to metatable - '%s' table`, tn); - this.xcMeta.metaUpdate(this.projectId, this.dbAlias, 'nc_models', { - schema: JSON.stringify(swaggerDoc) - }, { - title: tn - }) + this.log( + `mergeAndUpdateSwagger : Saving swagger JSON to metatable - '%s' table`, + tn + ); + this.xcMeta.metaUpdate( + this.projectId, + this.dbAlias, + 'nc_models', + { + schema: JSON.stringify(swaggerDoc) + }, + { + title: tn + } + ); } - protected async ncUpManyToMany(): Promise { const metas = await super.ncUpManyToMany(); if (!metas) { return; } for (const meta of metas) { - const ctx = this.generateContextForTable(meta.tn, meta.columns, [], meta.hasMany, meta.belongsTo, meta.type, meta._tn); + const ctx = this.generateContextForTable( + meta.tn, + meta.columns, + [], + meta.hasMany, + meta.belongsTo, + meta.type, + meta._tn + ); /* create routes for table */ - const routes = new ExpressXcTsRoutes({dir: '', ctx, filename: ''}).getObjectWithoutFunctions(); + const routes = new ExpressXcTsRoutes({ + dir: '', + ctx, + filename: '' + }).getObjectWithoutFunctions(); /* create nc_routes, add new routes or update order */ const routesInsertion = routes.map((route, i) => { return async () => { - if (!await this.xcMeta.metaGet(this.projectId, this.dbAlias, 'nc_routes', { - path: route.path, - tn: meta.tn, - title: meta.tn, - type: route.type - })) { - await this.xcMeta.metaInsert(this.projectId, this.dbAlias, 'nc_routes', { - acl: JSON.stringify(route.acl), - handler: JSON.stringify(route.handler), - order: i, - path: route.path, - tn: meta.tn, - title: meta.tn, - type: route.type, - }) + if ( + !(await this.xcMeta.metaGet( + this.projectId, + this.dbAlias, + 'nc_routes', + { + path: route.path, + tn: meta.tn, + title: meta.tn, + type: route.type + } + )) + ) { + await this.xcMeta.metaInsert( + this.projectId, + this.dbAlias, + 'nc_routes', + { + acl: JSON.stringify(route.acl), + handler: JSON.stringify(route.handler), + order: i, + path: route.path, + tn: meta.tn, + title: meta.tn, + type: route.type + } + ); } else { - await this.xcMeta.metaUpdate(this.projectId, this.dbAlias, 'nc_routes', { - order: i, - }, { - path: route.path, - tn: meta.tn, - title: meta.tn, - type: route.type - }) + await this.xcMeta.metaUpdate( + this.projectId, + this.dbAlias, + 'nc_routes', + { + order: i + }, + { + path: route.path, + tn: meta.tn, + title: meta.tn, + type: route.type + } + ); } - } + }; }); - await NcHelp.executeOperations(routesInsertion, this.connectionConfig.client); - + await NcHelp.executeOperations( + routesInsertion, + this.connectionConfig.client + ); } // add new routes - } public async onMetaUpdate(tn: string) { @@ -1788,15 +2519,22 @@ export class RestApiBuilder extends BaseApiBuilder { ); const swaggerDoc = await new SwaggerXc({ - dir: '', ctx: { + dir: '', + ctx: { ...ctx, v: this.metas[tn].v - }, filename: '' + }, + filename: '' }).getObject(); - const meta = await this.xcMeta.metaGet(this.projectId, this.dbAlias, 'nc_models', { - title: tn, - type: 'table' - }); + const meta = await this.xcMeta.metaGet( + this.projectId, + this.dbAlias, + 'nc_models', + { + title: tn, + type: 'table' + } + ); const oldSwaggerDoc = JSON.parse(meta.schema); // // keep upto 5 schema backup on table update @@ -1805,25 +2543,28 @@ export class RestApiBuilder extends BaseApiBuilder { // previousSchemas = [...JSON.parse(meta.schema_previous), oldSwaggerDoc].slice(-5); // } - oldSwaggerDoc.definitions = swaggerDoc.definitions; - await this.xcMeta.metaUpdate(this.projectId, this.dbAlias, 'nc_models', { - schema: JSON.stringify(oldSwaggerDoc), - // schema_previous: JSON.stringify(previousSchemas) - }, { - title: tn, - type: 'table' - }); + await this.xcMeta.metaUpdate( + this.projectId, + this.dbAlias, + 'nc_models', + { + schema: JSON.stringify(oldSwaggerDoc) + // schema_previous: JSON.stringify(previousSchemas) + }, + { + title: tn, + type: 'table' + } + ); await this.onSwaggerDocUpdate(tn); } - protected async getManyToManyRelations(args = {}): Promise> { const metas: Set = await super.getManyToManyRelations(args); for (const metaObj of metas) { - const ctx = this.generateContextForTable( metaObj.tn, metaObj.columns, @@ -1833,16 +2574,23 @@ export class RestApiBuilder extends BaseApiBuilder { ); const swaggerDoc = await new SwaggerXc({ - dir: '', ctx: { + dir: '', + ctx: { ...ctx, v: metaObj.v - }, filename: '' + }, + filename: '' }).getObject(); - const meta = await this.xcMeta.metaGet(this.projectId, this.dbAlias, 'nc_models', { - title: metaObj.tn, - type: 'table' - }); + const meta = await this.xcMeta.metaGet( + this.projectId, + this.dbAlias, + 'nc_models', + { + title: metaObj.tn, + type: 'table' + } + ); const oldSwaggerDoc = JSON.parse(meta.schema); // // keep upto 5 schema backup on table update @@ -1852,21 +2600,25 @@ export class RestApiBuilder extends BaseApiBuilder { // } oldSwaggerDoc.definitions = swaggerDoc.definitions; - await this.xcMeta.metaUpdate(this.projectId, this.dbAlias, 'nc_models', { - schema: JSON.stringify(oldSwaggerDoc), - // schema_previous: JSON.stringify(previousSchemas) - }, { - title: metaObj.tn, - type: 'table' - }); + await this.xcMeta.metaUpdate( + this.projectId, + this.dbAlias, + 'nc_models', + { + schema: JSON.stringify(oldSwaggerDoc) + // schema_previous: JSON.stringify(previousSchemas) + }, + { + title: metaObj.tn, + type: 'table' + } + ); } - return metas + return metas; } - } - /** * @copyright Copyright (c) 2021, Xgene Cloud Ltd * diff --git a/packages/nocodb/src/lib/noco/rest/RestAuthCtrl.ts b/packages/nocodb/src/lib/noco/rest/RestAuthCtrl.ts index 2584f85b4c..4f038c8953 100644 --- a/packages/nocodb/src/lib/noco/rest/RestAuthCtrl.ts +++ b/packages/nocodb/src/lib/noco/rest/RestAuthCtrl.ts @@ -1,68 +1,69 @@ -import {promisify} from 'util'; +import { promisify } from 'util'; import bcrypt from 'bcryptjs'; import * as ejs from 'ejs'; import * as jwt from 'jsonwebtoken'; -import {Tele} from 'nc-help'; +import { Tele } from 'nc-help'; import passport from 'passport'; -import {Strategy as AuthTokenStrategy} from 'passport-auth-token'; -import {Strategy as GithubStrategy} from 'passport-github' -import {Strategy as GoogleStrategy} from 'passport-google-oauth20'; -import {ExtractJwt, Strategy} from 'passport-jwt'; -import validator from "validator"; +import { Strategy as AuthTokenStrategy } from 'passport-auth-token'; +import { Strategy as GithubStrategy } from 'passport-github'; +import { Strategy as GoogleStrategy } from 'passport-google-oauth20'; +import { ExtractJwt, Strategy } from 'passport-jwt'; +import validator from 'validator'; -import {DbConfig, NcConfig} from "../../../interface/config"; -import {Knex} from '../../dataMapper'; -import Noco from "../Noco"; +import { DbConfig, NcConfig } from '../../../interface/config'; +import { Knex } from '../../dataMapper'; +import Noco from '../Noco'; const autoBind = require('auto-bind'); const PassportLocalStrategy = require('passport-local').Strategy; -const {v4: uuidv4} = require('uuid'); +const { v4: uuidv4 } = require('uuid'); -import * as crypto from "crypto"; +import * as crypto from 'crypto'; -import NcMetaIO from "../meta/NcMetaIO"; +import NcMetaIO from '../meta/NcMetaIO'; -const {isEmail} = require('validator'); +const { isEmail } = require('validator'); import axios from 'axios'; -import IEmailAdapter from "../../../interface/IEmailAdapter"; -import XcCache from "../plugins/adapters/cache/XcCache"; - -passport.serializeUser(function ({ - id, - email, - email_verified, - roles, - provider, - firstname, - lastname, - isAuthorized - }, done) { +import IEmailAdapter from '../../../interface/IEmailAdapter'; +import XcCache from '../plugins/adapters/cache/XcCache'; + +passport.serializeUser(function( + { + id, + email, + email_verified, + roles, + provider, + firstname, + lastname, + isAuthorized + }, + done +) { done(null, { isAuthorized, id, email, email_verified, provider, - firstname, lastname, + firstname, + lastname, roles: (roles || '') .split(',') - .reduce((obj, role) => Object.assign(obj, {[role]: true}), {}) - }) - ; + .reduce((obj, role) => Object.assign(obj, { [role]: true }), {}) + }); }); -passport.deserializeUser(function (user, done) { +passport.deserializeUser(function(user, done) { done(null, user); }); - const NC_ROLES = 'nc_roles'; const NC_ACL = 'nc_acl'; export default class RestAuthCtrl { - protected app: Noco; protected dbDriver: Knex; @@ -77,14 +78,19 @@ export default class RestAuthCtrl { public static instance: RestAuthCtrl; protected apiTokens: Array<{ - token: string, - [key: string]: any - }> + token: string; + [key: string]: any; + }>; // private router:Router; - - constructor(app: Noco, dbDriver: Knex, connectionConfig: DbConfig, config: NcConfig, xcMeta?: NcMetaIO) { + constructor( + app: Noco, + dbDriver: Knex, + connectionConfig: DbConfig, + config: NcConfig, + xcMeta?: NcMetaIO + ) { this.app = app; this.dbDriver = dbDriver; this.connectionConfig = connectionConfig; @@ -93,7 +99,10 @@ export default class RestAuthCtrl { autoBind(this); // todo: default secret generation this.config.auth.jwt.secret = this.config?.auth?.jwt?.secret ?? 'secret'; - this.jwtOptions = {secretOrKey: this.config.auth.jwt.secret, expiresIn: process.env.NC_JWT_EXPIRES_IN ?? '10h'} + this.jwtOptions = { + secretOrKey: this.config.auth.jwt.secret, + expiresIn: process.env.NC_JWT_EXPIRES_IN ?? '10h' + }; this.jwtOptions.jwtFromRequest = ExtractJwt.fromHeader('xc-auth'); if (this.config?.auth?.jwt?.options) { Object.assign(this.jwtOptions, this.config?.auth?.jwt?.options); @@ -103,7 +112,7 @@ export default class RestAuthCtrl { } get users(): any { - return this.dbDriver('xc_users') + return this.dbDriver('xc_users'); } async init() { @@ -113,65 +122,69 @@ export default class RestAuthCtrl { await this.initStrategies(); this.app.router.use(passport.initialize()); - const jwtMiddleware = passport.authenticate('jwt', {session: false}); + const jwtMiddleware = passport.authenticate('jwt', { session: false }); - this.app.router.get('/password/reset/:token', async function (req, res) { - res.send(ejs.render((await import('./ui/auth/resetPassword')).default, { - token: JSON.stringify(req.params?.token), - baseUrl: `/` - })); + this.app.router.get('/password/reset/:token', async function(req, res) { + res.send( + ejs.render((await import('./ui/auth/resetPassword')).default, { + token: JSON.stringify(req.params?.token), + baseUrl: `/` + }) + ); }); this.app.router.get('/email/verify/:token', async (req, res) => { - res.send(ejs.render((await import('./ui/auth/emailVerify')).default, { - token: JSON.stringify(req.params?.token), - baseUrl: `/` - })); + res.send( + ejs.render((await import('./ui/auth/emailVerify')).default, { + token: JSON.stringify(req.params?.token), + baseUrl: `/` + }) + ); }); this.app.router.get('/signin', async (_req, res) => { - res.send(ejs.render((await import('./ui/auth/signin')).default, { - baseUrl: `/` - })); + res.send( + ejs.render((await import('./ui/auth/signin')).default, { + baseUrl: `/` + }) + ); }); this.app.router.get('/signup', async (_req, res) => { - res.render(ejs.render((await import('./ui/auth/signup')).default, { - baseUrl: `/` - })); + res.render( + ejs.render((await import('./ui/auth/signup')).default, { + baseUrl: `/` + }) + ); }); - this.app.router.post(`/auth/signin`, this.signin) + this.app.router.post(`/auth/signin`, this.signin); - this.app.router.post(`/auth/signup`, this.signup) + this.app.router.post(`/auth/signup`, this.signup); this.app.router.post(`/auth/refresh-token`, this.refreshToken); - /* Google auth apis */ - this.app.router.post(`/auth/google/genTokenByCode`, this.googleSignin) + this.app.router.post(`/auth/google/genTokenByCode`, this.googleSignin); - this.app.router.get('/auth/google', - (req: any, res, next) => - passport.authenticate('google', { - scope: ['profile', 'email'], - state: req.query.state, - callbackURL: req.ncSiteUrl + this.config.dashboardPath - })(req, res, next) + this.app.router.get('/auth/google', (req: any, res, next) => + passport.authenticate('google', { + scope: ['profile', 'email'], + state: req.query.state, + callbackURL: req.ncSiteUrl + this.config.dashboardPath + })(req, res, next) ); /* Github auth apis */ - this.app.router.post(`/auth/github/genTokenByCode`, this.githubSignin) + this.app.router.post(`/auth/github/genTokenByCode`, this.githubSignin); - this.app.router.get('/auth/github', - (req: any, res, next) => - passport.authenticate('github', { - scope: ['profile', 'email'], - state: `github|${req.query.state || ''}`, - callbackURL: req.ncSiteUrl + this.config.dashboardPath - })(req, res, next) + this.app.router.get('/auth/github', (req: any, res, next) => + passport.authenticate('github', { + scope: ['profile', 'email'], + state: `github|${req.query.state || ''}`, + callbackURL: req.ncSiteUrl + this.config.dashboardPath + })(req, res, next) ); - /* this.app.router.get('/auth/azureadoauth2', passport.authenticate('azure_ad_oauth2')); @@ -184,47 +197,64 @@ export default class RestAuthCtrl { }); */ - - this.app.router.post(`/auth/password/forgot`, this.passwordForgot) - this.app.router.post(`/auth/token/validate/:tokenId`, this.tokenValidate) - this.app.router.post(`/auth/password/reset/:tokenId`, this.passwordReset) - this.app.router.post(`/user/password/change`, jwtMiddleware, this.passwordChange) - this.app.router.post(`/auth/email/validate/:tokenId` - , this.emailVerification) - this.app.router.put(`/user`, jwtMiddleware, this.updateUser) + this.app.router.post(`/auth/password/forgot`, this.passwordForgot); + this.app.router.post(`/auth/token/validate/:tokenId`, this.tokenValidate); + this.app.router.post(`/auth/password/reset/:tokenId`, this.passwordReset); + this.app.router.post( + `/user/password/change`, + jwtMiddleware, + this.passwordChange + ); + this.app.router.post( + `/auth/email/validate/:tokenId`, + this.emailVerification + ); + this.app.router.put(`/user`, jwtMiddleware, this.updateUser); // middleware for setting passport user( for treating non-authenticated user as guest) this.app.router.use(async (req, res, next) => { const user = await new Promise(resolve => { - passport.authenticate('jwt', {session: false}, (_err, user, _info) => { - if (user) { + passport.authenticate( + 'jwt', + { session: false }, + (_err, user, _info) => { + if (user) { + if ( + req.path.indexOf('/user/me') === -1 && + req.header('xc-preview') && + /(?:^|,)(?:owner|creator)(?:$|,)/.test(user.roles) + ) { + return resolve({ + ...user, + isAuthorized: true, + roles: req.header('xc-preview') + }); + } - if (req.path.indexOf('/user/me') === -1 && req.header('xc-preview') && /(?:^|,)(?:owner|creator)(?:$|,)/.test(user.roles)) { - return resolve({...user, isAuthorized: true, roles: req.header('xc-preview')}); + return resolve({ ...user, isAuthorized: true }); } - return resolve({...user, isAuthorized: true}); - } - - - if (req.headers['xc-token']) { - passport.authenticate( - 'authtoken', - { - session: false, - optional: false - }, (_err, user, _info) => { - if (user) { - return resolve({...user, isAuthorized: true}); - } else { - resolve({roles: 'guest'}) + if (req.headers['xc-token']) { + passport.authenticate( + 'authtoken', + { + session: false, + optional: false + }, + (_err, user, _info) => { + if (user) { + return resolve({ ...user, isAuthorized: true }); + } else { + resolve({ roles: 'guest' }); + } } - })(req, res, next); - } else { - resolve({roles: 'guest'}) + )(req, res, next); + } else { + resolve({ roles: 'guest' }); + } } - })(req, res, next); - }) + )(req, res, next); + }); await promisify((req as any).login.bind(req))(user); next(); @@ -239,56 +269,60 @@ export default class RestAuthCtrl { this.app.router.delete('/admin/roles/:id', this.deleteRole); this.app.router.put('/admin/roles', this.saveOrUpdateRoles); - this.app.router.post('/admin', this.addAdmin); this.app.router.put('/admin/:id', this.updateAdmin); this.app.router.delete('/admin/:id', this.deleteAdmin); this.app.router.get('/admin', this.listUsers); this.app.router.post('/admin/resendInvite/:id', this.resendInvite); - - } - public async initStrategies(): Promise { const self = this; - - passport.use('authtoken', new AuthTokenStrategy({headerFields: ['xc-token']}, (token, done) => { - const apiToken = this.apiTokens?.find(t => t.token === token) - if (apiToken) { - done(null, {roles: 'editor'}) - } else { - return done({msg: 'Invalid tok'}); - } - })); + passport.use( + 'authtoken', + new AuthTokenStrategy({ headerFields: ['xc-token'] }, (token, done) => { + const apiToken = this.apiTokens?.find(t => t.token === token); + if (apiToken) { + done(null, { roles: 'editor' }); + } else { + return done({ msg: 'Invalid tok' }); + } + }) + ); this.initJwtStrategy(); - - passport.use(new PassportLocalStrategy({ - usernameField: 'email', - session: false - }, async (email, password, done) => { - try { - const user = await self.users.where({email}).first(); - if (!user) { - return done({msg: `Email ${email} is not registered!`}); - } - const hashedPassword = await promisify(bcrypt.hash)(password, user.salt); - if (user.password !== hashedPassword) { - return done({msg: `Password not valid!`}); - } else { - return done(null, user); + passport.use( + new PassportLocalStrategy( + { + usernameField: 'email', + session: false + }, + async (email, password, done) => { + try { + const user = await self.users.where({ email }).first(); + if (!user) { + return done({ msg: `Email ${email} is not registered!` }); + } + const hashedPassword = await promisify(bcrypt.hash)( + password, + user.salt + ); + if (user.password !== hashedPassword) { + return done({ msg: `Password not valid!` }); + } else { + return done(null, user); + } + } catch (e) { + done(e); } - } catch (e) { - done(e); } - } - )); + ) + ); const googlePlugin = await this.xcMeta.metaGet(null, null, 'nc_plugins', { title: 'Google' - }) + }); if (googlePlugin && googlePlugin.input) { const settings = JSON.parse(googlePlugin.input); @@ -296,7 +330,10 @@ export default class RestAuthCtrl { process.env.NC_GOOGLE_CLIENT_SECRET = settings.client_secret; } - if (process.env.NC_GOOGLE_CLIENT_ID && process.env.NC_GOOGLE_CLIENT_SECRET) { + if ( + process.env.NC_GOOGLE_CLIENT_ID && + process.env.NC_GOOGLE_CLIENT_SECRET + ) { const googleAuthParamsOrig = GoogleStrategy.prototype.authorizationParams; GoogleStrategy.prototype.authorizationParams = (options: any) => { const params = googleAuthParamsOrig.call(this, options); @@ -308,53 +345,54 @@ export default class RestAuthCtrl { return params; }; - const clientConfig = { clientID: process.env.NC_GOOGLE_CLIENT_ID, clientSecret: process.env.NC_GOOGLE_CLIENT_SECRET, // todo: update url callbackURL: 'http://localhost:3000', passReqToCallback: true - } + }; - const googleStrategy = new GoogleStrategy(clientConfig, + const googleStrategy = new GoogleStrategy( + clientConfig, async (req, _accessToken, _refreshToken, profile, cb) => { try { const email = profile.emails[0].value; - let user = await this.users.where({ - email - }).first(); + let user = await this.users + .where({ + email + }) + .first(); const token = req.query.state; if (token) { - - Tele.emit('evt_subscribe', email) - await this.users.update({ - // firstname, lastname, - // email_verification_token, - invite_token: null, - invite_token_expires: null, - email_verified: true - }).where({ - email, - invite_token: token - }); + Tele.emit('evt_subscribe', email); + await this.users + .update({ + // firstname, lastname, + // email_verification_token, + invite_token: null, + invite_token_expires: null, + email_verified: true + }) + .where({ + email, + invite_token: token + }); } else { - - let roles = 'editor'; if (!(await this.users.first())) { - roles = 'owner,creator,editor' + roles = 'owner,creator,editor'; } if (!user) { if (roles === 'editor') { - return cb({msg: `Account not found!`}); + return cb({ msg: `Account not found!` }); } - Tele.emit('evt_subscribe', email) + Tele.emit('evt_subscribe', email); const salt = await promisify(bcrypt.genSalt)(10); user = await this.users.insert({ email: profile.emails[0].value, @@ -364,93 +402,104 @@ export default class RestAuthCtrl { email_verified: true }); } else { - await this.users.update({ - email_verified: true - }).where({ - email, - }); + await this.users + .update({ + email_verified: true + }) + .where({ + email + }); } - user = await this.users.where({ - email - }).first(); + user = await this.users + .where({ + email + }) + .first(); } cb(null, user); - } catch (e) { - cb(e, null) + cb(e, null); } - }); - + } + ); passport.use(googleStrategy); } - const githubPlugin = await this.xcMeta.metaGet(null, null, 'nc_plugins', { title: 'Github' - }) + }); if (githubPlugin && githubPlugin.input) { const settings = JSON.parse(githubPlugin.input); process.env.NC_GITHUB_CLIENT_ID = settings.client_id; process.env.NC_GITHUB_CLIENT_SECRET = settings.client_secret; } - if (process.env.NC_GITHUB_CLIENT_ID && process.env.NC_GITHUB_CLIENT_SECRET) { - const githubStrategy = new GithubStrategy({ + if ( + process.env.NC_GITHUB_CLIENT_ID && + process.env.NC_GITHUB_CLIENT_SECRET + ) { + const githubStrategy = new GithubStrategy( + { clientID: process.env.NC_GITHUB_CLIENT_ID, clientSecret: process.env.NC_GITHUB_CLIENT_SECRET, // callbackURL: app.$config.auth.github.callbackUrl, passReqToCallback: true - }, async (req, accessToken, _refreshToken, profile, cb) => { + }, + async (req, accessToken, _refreshToken, profile, cb) => { try { - - let email = profile.emails && profile.emails[0] && profile.emails[0].value; + let email = + profile.emails && profile.emails[0] && profile.emails[0].value; if (!email) { const res = await axios.get('https://api.github.com/user', { headers: { - 'Authorization': 'token ' + accessToken + Authorization: 'token ' + accessToken } - }) + }); if (res.data && res.data.length) { email = res.data[0].email; } else { - return cb(null, false, {message: 'There is no email id associated to your github account.'}) + return cb(null, false, { + message: + 'There is no email id associated to your github account.' + }); } } - let user = await this.users.where({ - email - }).first(); + let user = await this.users + .where({ + email + }) + .first(); const token = req.query?.state?.replace('github|', ''); if (token) { - - Tele.emit('evt_subscribe', email) - await this.users.update({ - // firstname, lastname, - // email_verification_token, - invite_token: null, - invite_token_expires: null, - email_verified: true - }).where({ - email, - invite_token: token - }); + Tele.emit('evt_subscribe', email); + await this.users + .update({ + // firstname, lastname, + // email_verification_token, + invite_token: null, + invite_token_expires: null, + email_verified: true + }) + .where({ + email, + invite_token: token + }); } else { - - let roles = 'editor'; if (!(await this.users.first())) { - roles = 'owner,creator,editor' + roles = 'owner,creator,editor'; } if (!user) { if (roles === 'editor') { - return cb({msg: `Account not found!`}); + return cb({ msg: `Account not found!` }); } - Tele.emit('evt_subscribe', email) + Tele.emit('evt_subscribe', email); const salt = await promisify(bcrypt.genSalt)(10); user = await this.users.insert({ email: profile.emails[0].value, @@ -460,30 +509,30 @@ export default class RestAuthCtrl { email_verified: true }); } else { - await this.users.update({ - email_verified: true - }).where({ - email, - }); + await this.users + .update({ + email_verified: true + }) + .where({ + email + }); } - user = await this.users.where({ - email - }).first(); + user = await this.users + .where({ + email + }) + .first(); } cb(null, user); - } catch (e) { - cb(e, null) + cb(e, null); } } ); - passport.use(githubStrategy); - } - /* passport.use(new AzureAdOAuth2Strategy({ }, @@ -495,227 +544,269 @@ export default class RestAuthCtrl { // this is just an example: here you would provide a model *User* with the function *findOrCreate* done(waadProfile) }));*/ - - } protected initJwtStrategy() { - passport.use(new Strategy(this.jwtOptions, (jwtPayload, done) => { - this.users.where({ - email: jwtPayload?.email - }).first().then(user => { - if (user) { - user.roles = 'owner,creator' - return done(null, user); - } else { - return done(new Error('User not found')); - } - }).catch(err => { - return done(err); + passport.use( + new Strategy(this.jwtOptions, (jwtPayload, done) => { + this.users + .where({ + email: jwtPayload?.email + }) + .first() + .then(user => { + if (user) { + user.roles = 'owner,creator'; + return done(null, user); + } else { + return done(new Error('User not found')); + } + }) + .catch(err => { + return done(err); + }); }) - })); + ); } protected async signin(req, res, next): Promise { - passport.authenticate('local', {session: false}, async (err, user, info): Promise => { - try { - if (!user || !user.email) { - if (err) { - return res.status(400).send(err) - } - if (info) { - return res.status(400).send(info) + passport.authenticate( + 'local', + { session: false }, + async (err, user, info): Promise => { + try { + if (!user || !user.email) { + if (err) { + return res.status(400).send(err); + } + if (info) { + return res.status(400).send(info); + } + return res.status(400).send({ msg: 'Your signin has failed' }); } - return res.status(400).send({msg: 'Your signin has failed'}); - } - await promisify((req as any).login.bind(req))(user); - const refreshToken = this.randomTokenString(); + await promisify((req as any).login.bind(req))(user); + const refreshToken = this.randomTokenString(); - await this.users.update({ - refresh_token: refreshToken - }).where({id: user.id}) + await this.users + .update({ + refresh_token: refreshToken + }) + .where({ id: user.id }); - this.setTokenCookie(res, refreshToken); + this.setTokenCookie(res, refreshToken); - this.xcMeta.audit(null, null, 'nc_audit', { - op_type: 'AUTHENTICATION', - op_sub_type: 'SIGNIN', - user: user.email, - ip: req.clientIp, - description: `signed in` - }) + this.xcMeta.audit(null, null, 'nc_audit', { + op_type: 'AUTHENTICATION', + op_sub_type: 'SIGNIN', + user: user.email, + ip: req.clientIp, + description: `signed in` + }); - res.json({ - token: jwt.sign({ - email: user.email, - firstname: user.firstname, - lastname: user.lastname, - id: user.id, - roles: user.roles - }, this.config.auth.jwt.secret, - this.config.auth.jwt.options) - } as any); - } catch (e) { - console.log(e); - throw e; + res.json({ + token: jwt.sign( + { + email: user.email, + firstname: user.firstname, + lastname: user.lastname, + id: user.id, + roles: user.roles + }, + this.config.auth.jwt.secret, + this.config.auth.jwt.options + ) + } as any); + } catch (e) { + console.log(e); + throw e; + } } - })(req, res, next); + )(req, res, next); } - protected async googleSignin(req, res, next): Promise { - passport.authenticate('google', { - session: false, - callbackURL: req.ncSiteUrl + this.config.dashboardPath - }, async (err, user, info): Promise => { - try { - if (!user || !user.email) { - if (err) { - return res.status(400).send(err) - } - if (info) { - return res.status(400).send(info) + passport.authenticate( + 'google', + { + session: false, + callbackURL: req.ncSiteUrl + this.config.dashboardPath + }, + async (err, user, info): Promise => { + try { + if (!user || !user.email) { + if (err) { + return res.status(400).send(err); + } + if (info) { + return res.status(400).send(info); + } + return res.status(500).send({ msg: 'Your signin has failed' }); } - return res.status(500).send({msg: 'Your signin has failed'}); - } - await promisify((req as any).login.bind(req))(user); - const refreshToken = this.randomTokenString(); + await promisify((req as any).login.bind(req))(user); + const refreshToken = this.randomTokenString(); - await this.users.update({ - refresh_token: refreshToken - }).where({id: user.id}) + await this.users + .update({ + refresh_token: refreshToken + }) + .where({ id: user.id }); - this.setTokenCookie(res, refreshToken); + this.setTokenCookie(res, refreshToken); - this.xcMeta.audit(null, null, 'nc_audit', { - op_type: 'AUTHENTICATION', - op_sub_type: 'SIGNIN', - user: user.email, - ip: req.clientIp, - description: `signed in using Google Auth` - }) + this.xcMeta.audit(null, null, 'nc_audit', { + op_type: 'AUTHENTICATION', + op_sub_type: 'SIGNIN', + user: user.email, + ip: req.clientIp, + description: `signed in using Google Auth` + }); - res.json({ - token: jwt.sign({ - email: user.email, - firstname: user.firstname, - lastname: user.lastname, - id: user.id, - roles: user.roles - }, this.config.auth.jwt.secret, this.config.auth.jwt.options) - } as any); - } catch (e) { - console.log(e); - throw e; + res.json({ + token: jwt.sign( + { + email: user.email, + firstname: user.firstname, + lastname: user.lastname, + id: user.id, + roles: user.roles + }, + this.config.auth.jwt.secret, + this.config.auth.jwt.options + ) + } as any); + } catch (e) { + console.log(e); + throw e; + } } - })(req, res, next); + )(req, res, next); } - protected async githubSignin(req, res, next): Promise { - passport.authenticate('github', { - session: false, - callbackURL: req.ncSiteUrl + this.config.dashboardPath - }, async (err, user, info): Promise => { - try { - if (!user || !user.email) { - if (err) { - return res.status(400).send(err) - } - if (info) { - return res.status(400).send(info) + passport.authenticate( + 'github', + { + session: false, + callbackURL: req.ncSiteUrl + this.config.dashboardPath + }, + async (err, user, info): Promise => { + try { + if (!user || !user.email) { + if (err) { + return res.status(400).send(err); + } + if (info) { + return res.status(400).send(info); + } + return res.status(500).send({ msg: 'Your signin has failed' }); } - return res.status(500).send({msg: 'Your signin has failed'}); - } - await promisify((req as any).login.bind(req))(user); - const refreshToken = this.randomTokenString(); + await promisify((req as any).login.bind(req))(user); + const refreshToken = this.randomTokenString(); - await this.users.update({ - refresh_token: refreshToken - }).where({id: user.id}) + await this.users + .update({ + refresh_token: refreshToken + }) + .where({ id: user.id }); - this.setTokenCookie(res, refreshToken); + this.setTokenCookie(res, refreshToken); - res.json({ - token: jwt.sign({ - email: user.email, - firstname: user.firstname, - lastname: user.lastname, - id: user.id, - roles: user.roles - }, this.config.auth.jwt.secret, this.config.auth.jwt.options) - } as any); - } catch (e) { - console.log(e); - throw e; + res.json({ + token: jwt.sign( + { + email: user.email, + firstname: user.firstname, + lastname: user.lastname, + id: user.id, + roles: user.roles + }, + this.config.auth.jwt.secret, + this.config.auth.jwt.options + ) + } as any); + } catch (e) { + console.log(e); + throw e; + } } - })(req, res, next); + )(req, res, next); } - protected async refreshToken(req, res): Promise { - console.log('token refresh') + console.log('token refresh'); try { - if (!req?.cookies?.refresh_token) { - return res.status(400).json({msg: 'Missing refresh token'}); + return res.status(400).json({ msg: 'Missing refresh token' }); } - const user = await this.users.where({ - refresh_token: req.cookies.refresh_token - }).first(); + const user = await this.users + .where({ + refresh_token: req.cookies.refresh_token + }) + .first(); if (!user) { - return res.status(400).json({msg: 'Invalid refresh token'}); + return res.status(400).json({ msg: 'Invalid refresh token' }); } const refreshToken = this.randomTokenString(); - await this.users.update({ - refresh_token: refreshToken - }).where({ - id: user.id - }); + await this.users + .update({ + refresh_token: refreshToken + }) + .where({ + id: user.id + }); this.setTokenCookie(res, refreshToken); res.json({ - token: jwt.sign({ - email: user.email, - firstname: user.firstname, - lastname: user.lastname, - id: user.id, - roles: user.roles - }, this.config.auth.jwt.secret, this.config.auth.jwt.options) + token: jwt.sign( + { + email: user.email, + firstname: user.firstname, + lastname: user.lastname, + id: user.id, + roles: user.roles + }, + this.config.auth.jwt.secret, + this.config.auth.jwt.options + ) } as any); } catch (e) { - return res.status(400).json({msg: e.message}); + return res.status(400).json({ msg: e.message }); } } protected async signup(req, res, next): Promise { - try { - const {email, firstname, lastname, token, ignore_subscribe} = req.body; - let {password} = req.body; + const { email, firstname, lastname, token, ignore_subscribe } = req.body; + let { password } = req.body; if (!isEmail(email)) { return next(new Error(`Invalid email`)); } - let user = await this.users.where({ - email - }).first(); + let user = await this.users + .where({ + email + }) + .first(); if (user) { if (token) { if (token !== user.invite_token) { return next(new Error(`Invalid invite url`)); } else if (user.invite_token_expires < new Date()) { - return next(new Error('Expired invite url, Please contact super admin to get a new invite url')); + return next( + new Error( + 'Expired invite url, Please contact super admin to get a new invite url' + ) + ); } } else { // todo : opening up signup for timebeing @@ -727,44 +818,47 @@ export default class RestAuthCtrl { password = await promisify(bcrypt.hash)(password, salt); const email_verification_token = uuidv4(); - if (!ignore_subscribe) { - Tele.emit('evt_subscribe', email) + Tele.emit('evt_subscribe', email); } if (user) { if (token) { - await this.users.update({ - firstname, lastname, - salt, - password, - email_verification_token, - invite_token: null, - invite_token_expires: null - }).where({ - email, - invite_token: token - }); + await this.users + .update({ + firstname, + lastname, + salt, + password, + email_verification_token, + invite_token: null, + invite_token_expires: null + }) + .where({ + email, + invite_token: token + }); } else { - return next(new Error('User already exist')) + return next(new Error('User already exist')); } } else { - - let roles = 'user'; if (!(await this.users.first())) { // roles = 'owner,creator,editor' } else { if (process.env.NC_INVITE_ONLY_SIGNUP) { - return next(new Error('Not allowed to signup, contact super admin.')); + return next( + new Error('Not allowed to signup, contact super admin.') + ); } else { roles = 'user_new'; } } await this.users.insert({ - firstname, lastname, + firstname, + lastname, email, salt, password, @@ -772,29 +866,36 @@ export default class RestAuthCtrl { roles }); } - user = await this.users.where({ - email - }).first(); + user = await this.users + .where({ + email + }) + .first(); try { const template = (await import('./ui/emailTemplates/verify')).default; await this.emailClient.mailSend({ to: email, - subject: "Verify email", + subject: 'Verify email', html: ejs.render(template, { - verifyLink: req.ncSiteUrl + `/email/verify/${user.email_verification_token}` + verifyLink: + req.ncSiteUrl + `/email/verify/${user.email_verification_token}` }) - }) + }); } catch (e) { - console.log('Warning : `mailSend` failed, Please configure emailClient configuration.'); + console.log( + 'Warning : `mailSend` failed, Please configure emailClient configuration.' + ); } await promisify((req as any).login.bind(req))(user); const refreshToken = this.randomTokenString(); - await this.users.update({ - refresh_token: refreshToken - }).where({ - id: user.id - }) + await this.users + .update({ + refresh_token: refreshToken + }) + .where({ + id: user.id + }); this.setTokenCookie(res, refreshToken); @@ -804,17 +905,21 @@ export default class RestAuthCtrl { op_type: 'AUTHENTICATION', op_sub_type: 'SIGNUP', user: user.email, - description: `signed up `, ip: req.clientIp - }) + description: `signed up `, + ip: req.clientIp + }); res.json({ - token: jwt.sign({ - email: user.email, - firstname: user.firstname, - lastname: user.lastname, - id: user.id, - roles: user.roles - }, this.config.auth.jwt.secret) + token: jwt.sign( + { + email: user.email, + firstname: user.firstname, + lastname: user.lastname, + id: user.id, + roles: user.roles + }, + this.config.auth.jwt.secret + ) } as any); } catch (e) { console.log(e); @@ -828,47 +933,53 @@ export default class RestAuthCtrl { return next(new Error('Please enter your email address.')); } - const user = await this.users.where({email}).first(); + const user = await this.users.where({ email }).first(); if (!user) { return next(new Error('This email is not registered with us.')); } const token = uuidv4(); - await this.users.update({ - reset_password_token: token, - reset_password_expires: new Date(Date.now() + (60 * 60 * 1000)) - }).where({id: user.id}); + await this.users + .update({ + reset_password_token: token, + reset_password_expires: new Date(Date.now() + 60 * 60 * 1000) + }) + .where({ id: user.id }); try { - const template = (await import( './ui/emailTemplates/forgotPassword')).default + const template = (await import('./ui/emailTemplates/forgotPassword')) + .default; await this.emailClient.mailSend({ to: user.email, - subject: "Password Reset Link", + subject: 'Password Reset Link', text: `Visit following link to update your password : ${req.ncSiteUrl}/password/reset/${token}.`, html: ejs.render(template, { resetLink: req.ncSiteUrl + `/password/reset/${token}` }) - }) + }); } catch (e) { - console.log('Warning : `mailSend` failed, Please configure emailClient configuration.'); + console.log( + 'Warning : `mailSend` failed, Please configure emailClient configuration.' + ); } - console.log(`Password reset token : ${token}`) - + console.log(`Password reset token : ${token}`); this.xcMeta.audit(null, null, 'nc_audit', { op_type: 'AUTHENTICATION', op_sub_type: 'PASSWORD_FORGOT', user: user.email, - description: `requested for password reset `, ip: req.clientIp - }) - - res.json({msg: 'Check your email for password reset link.'}); + description: `requested for password reset `, + ip: req.clientIp + }); + res.json({ msg: 'Check your email for password reset link.' }); } protected async tokenValidate(req, res, next): Promise { const token = req.params.tokenId; - const user = await this.users.where({reset_password_token: token}).first(); + const user = await this.users + .where({ reset_password_token: token }) + .first(); if (!user || !user.email) { return next(new Error('Invalid reset url')); } @@ -878,10 +989,11 @@ export default class RestAuthCtrl { res.json(true); } - protected async passwordReset(req, res, next): Promise { const token = req.params.tokenId; - const user = await this.users.where({reset_password_token: token}).first(); + const user = await this.users + .where({ reset_password_token: token }) + .first(); if (!user) { return next(new Error('Invalid reset url')); } @@ -895,34 +1007,39 @@ export default class RestAuthCtrl { const salt = await promisify(bcrypt.genSalt)(10); const password = await promisify(bcrypt.hash)(req.body.password, salt); - await this.users.update({ - salt, password, - reset_password_expires: null, - reset_password_token: '' - }).where({ - id: user.id - }); + await this.users + .update({ + salt, + password, + reset_password_expires: null, + reset_password_token: '' + }) + .where({ + id: user.id + }); this.xcMeta.audit(null, null, 'nc_audit', { op_type: 'AUTHENTICATION', op_sub_type: 'PASSWORD_RESET', user: user.email, - description: `did reset password `, ip: req.clientIp - }) + description: `did reset password `, + ip: req.clientIp + }); - res.json({msg: 'Password reset successful'}) + res.json({ msg: 'Password reset successful' }); } - protected async passwordChange(req, res, next): Promise { - - const {currentPassword, newPassword} = req.body; + const { currentPassword, newPassword } = req.body; if (req.isAuthenticated()) { if (!currentPassword || !newPassword) { - return next(new Error('Missing new/old password')) + return next(new Error('Missing new/old password')); } - const user = await this.users.where({email: req.user.email}).first(); - const hashedPassword = await promisify(bcrypt.hash)(currentPassword, user.salt); + const user = await this.users.where({ email: req.user.email }).first(); + const hashedPassword = await promisify(bcrypt.hash)( + currentPassword, + user.salt + ); if (hashedPassword !== user.password) { return next(new Error('Current password is wrong')); } @@ -930,62 +1047,68 @@ export default class RestAuthCtrl { const salt = await promisify(bcrypt.genSalt)(10); const password = await promisify(bcrypt.hash)(newPassword, salt); - await this.users.update({ - salt, password - }).where({id: user.id}); - + await this.users + .update({ + salt, + password + }) + .where({ id: user.id }); this.xcMeta.audit(null, null, 'nc_audit', { op_type: 'AUTHENTICATION', op_sub_type: 'PASSWORD_CHANGE', user: user.email, - description: `changed password `, ip: req.clientIp - }) - - res.json({msg: 'Password updated successfully'}) + description: `changed password `, + ip: req.clientIp + }); + res.json({ msg: 'Password updated successfully' }); } } - protected async emailVerification(req, res, next): Promise { const token = req.params.tokenId; - const user = await this.users.where({email_verification_token: token}).first(); + const user = await this.users + .where({ email_verification_token: token }) + .first(); if (!user) { return next(new Error('Invalid verification url')); } - await this.users.update({ - email_verification_token: '', - email_verified: true - }).where({id: user.id}); + await this.users + .update({ + email_verification_token: '', + email_verified: true + }) + .where({ id: user.id }); this.xcMeta.audit(null, null, 'nc_audit', { op_type: 'AUTHENTICATION', op_sub_type: 'EMAIL_VERIFICATION', user: user.email, - description: `verified email `, ip: req.clientIp - }) + description: `verified email `, + ip: req.clientIp + }); - res.json({msg: 'Email verified successfully'}); + res.json({ msg: 'Email verified successfully' }); } - protected async me(req, res): Promise { res.json(req?.session?.passport?.user ?? {}); } protected async updateUser(req, res): Promise { - await this.users.update({ - firstname: req.body.firstname, - lastname: req.body.lastname, - }).where({ - id: req.user.id - }) - res.json({msg: 'Updated successfully'}); + await this.users + .update({ + firstname: req.body.firstname, + lastname: req.body.lastname + }) + .where({ + id: req.user.id + }); + res.json({ msg: 'Updated successfully' }); } - /* Admin apis : START */ // @ts-ignore @@ -993,19 +1116,21 @@ export default class RestAuthCtrl { if (req.session?.passport?.user?.roles?.owner) { return next(); } - res.status(401).json({msg: 'Access denied'}) + res.status(401).json({ msg: 'Access denied' }); } protected async isAdmin(req, res, next): Promise { - if (req.session?.passport?.user?.roles?.owner || req.session?.passport?.user?.roles?.creator || req?.session?.passport?.user?.roles?.editor) { + if ( + req.session?.passport?.user?.roles?.owner || + req.session?.passport?.user?.roles?.creator || + req?.session?.passport?.user?.roles?.editor + ) { return next(); } - res.status(403).json({msg: 'Access denied'}) + res.status(403).json({ msg: 'Access denied' }); } - protected async addAdmin(req, res, next): Promise { - // if (!this.config?.mailer || !this.emailClient) { // return next(new Error('SMTP config is not found')); // } @@ -1017,71 +1142,93 @@ export default class RestAuthCtrl { } // todo: handle roles which contains super - if (!req.session?.passport?.user?.roles?.owner && req.body.roles.indexOf('owner') > -1) { + if ( + !req.session?.passport?.user?.roles?.owner && + req.body.roles.indexOf('owner') > -1 + ) { return next(new Error('Insufficient privilege to add super admin role.')); } const invite_token = uuidv4(); let count; - const user = await this.users.where({email}).first(); + const user = await this.users.where({ email }).first(); if (user) { - if (!await this.xcMeta.isUserHaveAccessToProject(req.body.project_id, user.id)) { - await this.xcMeta.projectAddUser(req.body.project_id, user.id, 'creator'); + if ( + !(await this.xcMeta.isUserHaveAccessToProject( + req.body.project_id, + user.id + )) + ) { + await this.xcMeta.projectAddUser( + req.body.project_id, + user.id, + 'creator' + ); } } else { try { await this.users.insert({ invite_token, - invite_token_expires: new Date(Date.now() + (24 * 60 * 60 * 1000)), + invite_token_expires: new Date(Date.now() + 24 * 60 * 60 * 1000), email }); count = await this.users.count('id').first(); - const {id} = await this.users.where({email}).first(); + const { id } = await this.users.where({ email }).first(); await this.xcMeta.projectAddUser(req.body.project_id, id, 'creator'); - if (!await this.sendInviteEmail(email, invite_token, req)) { - res.json({invite_token, email}) + if (!(await this.sendInviteEmail(email, invite_token, req))) { + res.json({ invite_token, email }); } } catch (e) { return next(e); } } - - Tele.emit('evt', {evt_type: 'project:invite', count: count?.count}) + Tele.emit('evt', { evt_type: 'project:invite', count: count?.count }); this.xcMeta.audit(req.body.project_id, null, 'nc_audit', { op_type: 'AUTHENTICATION', op_sub_type: 'INVITE', user: req.user.email, - description: `invited ${email} to ${req.body.project_id} project `, ip: req.clientIp - }) + description: `invited ${email} to ${req.body.project_id} project `, + ip: req.clientIp + }); res.json({ msg: 'success' - }) + }); } protected async updateAdmin(req, res, next): Promise { - if (!req?.body?.project_id) { return next(new Error('Missing project id in request body.')); } - if (req.session?.passport?.user?.roles?.owner && req.session?.passport?.user?.id === +req.params.id && req.body.roles.indexOf('owner') === -1) { - return next(new Error('Super admin can\'t remove Super role themselves')); + if ( + req.session?.passport?.user?.roles?.owner && + req.session?.passport?.user?.id === +req.params.id && + req.body.roles.indexOf('owner') === -1 + ) { + return next(new Error("Super admin can't remove Super role themselves")); } try { - const user = await this.users.where({ - id: req.params.id - }).first(); + const user = await this.users + .where({ + id: req.params.id + }) + .first(); if (!user) { return next(`User with id '${req.params.id}' doesn't exist`); } // todo: handle roles which contains super - if (!req.session?.passport?.user?.roles?.owner && req.body.roles.indexOf('owner') > -1) { - return next(new Error('Insufficient privilege to add super admin role.')); + if ( + !req.session?.passport?.user?.roles?.owner && + req.body.roles.indexOf('owner') > -1 + ) { + return next( + new Error('Insufficient privilege to add super admin role.') + ); } // await this.users.update({ @@ -1089,67 +1236,77 @@ export default class RestAuthCtrl { // }).where({ // id: req.params.id // }); - await this.xcMeta.metaUpdate(req?.body?.project_id, null, 'nc_projects_users', { - roles: 'creator' - }, { - user_id: req.params.id - }) - + await this.xcMeta.metaUpdate( + req?.body?.project_id, + null, + 'nc_projects_users', + { + roles: 'creator' + }, + { + user_id: req.params.id + } + ); this.xcMeta.audit(null, null, 'nc_audit', { op_type: 'AUTHENTICATION', op_sub_type: 'ROLES_MANAGEMENT', user: req.user.email, - description: `updated roles for ${user.email} with ${req.body.roles} `, ip: req.clientIp - }) + description: `updated roles for ${user.email} with ${req.body.roles} `, + ip: req.clientIp + }); res.json({ msg: 'User details updated successfully' - }) - + }); } catch (e) { next(e); } - } protected async deleteAdmin(req, res, next): Promise { try { - const {project_id} = req.query; + const { project_id } = req.query; if (req.session?.passport?.user?.id === +req.params.id) { - return next(new Error('Admin can\'t delete themselves!')); + return next(new Error("Admin can't delete themselves!")); } if (!req.session?.passport?.user?.roles?.owner) { - const deleteUser = await this.users.where('id', req.params.id).andWhere('roles', 'like', '%super%').first(); + const deleteUser = await this.users + .where('id', req.params.id) + .andWhere('roles', 'like', '%super%') + .first(); if (deleteUser) { - return next(new Error('Insufficient privilege to delete a super admin user.')); + return next( + new Error('Insufficient privilege to delete a super admin user.') + ); } } - XcCache.del(`${req?.query?.email}___${req?.req?.project_id}`); // await this.users.where('id', req.params.id).del(); await this.xcMeta.projectRemoveUser(project_id, req.params.id); } catch (e) { - return next(e) + return next(e); } res.json({ msg: 'success' - }) - + }); } protected async listUsers(req, res, next): Promise { - - try { - const {offset = 0, limit = 20, query, project_id} = req.query; + const { offset = 0, limit = 20, query, project_id } = req.query; let count; - const queryBuilder = this.users.select('xc_users.*', 'nc_projects_users.project_id', 'nc_projects_users.roles as roles') + const queryBuilder = this.users + .select( + 'xc_users.*', + 'nc_projects_users.project_id', + 'nc_projects_users.roles as roles' + ) .offset(offset) .limit(limit); @@ -1157,33 +1314,48 @@ export default class RestAuthCtrl { queryBuilder.where('email', 'like', `%${query}%`); } const self = this; - queryBuilder.leftJoin('nc_projects_users', function () { - this.on('nc_projects_users.user_id', '=', 'xc_users.id').andOn('nc_projects_users.project_id', '=', self.xcMeta.knex.raw('?', [project_id])) + queryBuilder.leftJoin('nc_projects_users', function() { + this.on('nc_projects_users.user_id', '=', 'xc_users.id').andOn( + 'nc_projects_users.project_id', + '=', + self.xcMeta.knex.raw('?', [project_id]) + ); }); if (!req.session?.passport?.user?.roles?.owner) { - queryBuilder.whereNot('nc_projects_users.roles', 'like', '%owner%') - count = (await this.users.count('id as count').whereNot('roles', 'like', '%owner%').first()).count; + queryBuilder.whereNot('nc_projects_users.roles', 'like', '%owner%'); + count = ( + await this.users + .count('id as count') + .whereNot('roles', 'like', '%owner%') + .first() + ).count; } else { - count = (await this.users.count('id as count').where('email', 'like', `%${query}%`).first()).count; + count = ( + await this.users + .count('id as count') + .where('email', 'like', `%${query}%`) + .first() + ).count; } - const list = (await queryBuilder).map(({password, salt, refresh_token, ...rest}) => rest); + const list = (await queryBuilder).map( + ({ password, salt, refresh_token, ...rest }) => rest + ); res.json({ list, count, offset, limit - }) + }); } catch (e) { - next(e) + next(e); } - } protected async resendInvite(req, res, next): Promise { try { - const user = await this.users.where({id: req.params.id}).first(); + const user = await this.users.where({ id: req.params.id }).first(); if (!user) { return next(new Error(`User with id '${req.params.id}' not found`)); @@ -1192,28 +1364,30 @@ export default class RestAuthCtrl { req.body.roles = user.roles; const invite_token = uuidv4(); - await this.users.update({ - invite_token, - invite_token_expires: new Date(Date.now() + (24 * 60 * 60 * 1000)), - }).where({ - id: user.id - }) - await this.sendInviteEmail(user.email, invite_token, req) + await this.users + .update({ + invite_token, + invite_token_expires: new Date(Date.now() + 24 * 60 * 60 * 1000) + }) + .where({ + id: user.id + }); + await this.sendInviteEmail(user.email, invite_token, req); this.xcMeta.audit(null, null, 'nc_audit', { op_type: 'AUTHENTICATION', op_sub_type: 'RESEND_INVITE', user: user.email, - description: `resent a invite to ${user.email} `, ip: req.clientIp - }) + description: `resent a invite to ${user.email} `, + ip: req.clientIp + }); - res.json({msg: 'success'}) + res.json({ msg: 'success' }); } catch (e) { next(e); } } - protected async sendInviteEmail(email, token, req): Promise { try { const template = (await import('./ui/emailTemplates/invite')).default; @@ -1221,15 +1395,18 @@ export default class RestAuthCtrl { if (this.emailClient) { await this.emailClient.mailSend({ to: email, - subject: "Verify email", + subject: 'Verify email', html: ejs.render(template, { signupLink: `${req.ncSiteUrl}${this.config?.dashboardPath}#/user/authentication/signup/${token}`, projectName: req.body?.projectName, - roles: (req.body?.roles || '').split(',').map(r => r.replace(/^./, m => m.toUpperCase())).join(', '), + roles: (req.body?.roles || '') + .split(',') + .map(r => r.replace(/^./, m => m.toUpperCase())) + .join(', '), adminEmail: req.session?.passport?.user?.email }) - }) - return true + }); + return true; } // throw new Error('SMTP not configured, sending email failed') // } else { @@ -1241,7 +1418,10 @@ export default class RestAuthCtrl { // }) // } } catch (e) { - console.log('Warning : `mailSend` failed, Please configure emailClient configuration.', e.message); + console.log( + 'Warning : `mailSend` failed, Please configure emailClient configuration.', + e.message + ); throw e; } } @@ -1255,26 +1435,45 @@ export default class RestAuthCtrl { id: role.id }); - if (oldRole.title !== role.title || oldRole.description !== role.description) { - await this.xcMeta.metaUpdate('', '', NC_ROLES, { - ...role - }, { - id: role.id - }); + if ( + oldRole.title !== role.title || + oldRole.description !== role.description + ) { + await this.xcMeta.metaUpdate( + '', + '', + NC_ROLES, + { + ...role + }, + { + id: role.id + } + ); } if (oldRole.title !== role.title) { for (const builder of (this.app as Noco).getBuilders()) { try { await this.xcMeta.startTransaction(); - const aclRows = await this.xcMeta.metaList('', builder.getDbAlias(), NC_ACL); + const aclRows = await this.xcMeta.metaList( + '', + builder.getDbAlias(), + NC_ACL + ); for (const aclRow of aclRows) { if (aclRow.acl) { const acl = JSON.parse(aclRow.acl); acl[role.title] = acl[oldRole.title]; delete acl[oldRole.title]; - await this.xcMeta.metaUpdate('', builder.getDbAlias(), NC_ACL, { - acl: JSON.stringify(acl) - }, aclRow.id); + await this.xcMeta.metaUpdate( + '', + builder.getDbAlias(), + NC_ACL, + { + acl: JSON.stringify(acl) + }, + aclRow.id + ); } } await this.xcMeta.commit(); @@ -1284,8 +1483,10 @@ export default class RestAuthCtrl { } } } else { - if ((await this.xcMeta.metaGet('', '', NC_ROLES, {title: role.title}))) { - return next(Error(`Role name '${role.title}' already exist`)) + if ( + await this.xcMeta.metaGet('', '', NC_ROLES, { title: role.title }) + ) { + return next(Error(`Role name '${role.title}' already exist`)); } await this.xcMeta.metaInsert('', '', NC_ROLES, role); @@ -1293,16 +1494,26 @@ export default class RestAuthCtrl { for (const builder of (this.app as Noco).getBuilders()) { try { await this.xcMeta.startTransaction(); - const aclRows = await this.xcMeta.metaList('', builder.getDbAlias(), NC_ACL); + const aclRows = await this.xcMeta.metaList( + '', + builder.getDbAlias(), + NC_ACL + ); for (const aclRow of aclRows) { if (aclRow.acl) { const acl = JSON.parse(aclRow.acl); acl[role.title] = true; - await this.xcMeta.metaUpdate('', builder.getDbAlias(), NC_ACL, { - acl: JSON.stringify(acl) - }, { - id: aclRow.id - }); + await this.xcMeta.metaUpdate( + '', + builder.getDbAlias(), + NC_ACL, + { + acl: JSON.stringify(acl) + }, + { + id: aclRow.id + } + ); } } await this.xcMeta.commit(); @@ -1313,24 +1524,26 @@ export default class RestAuthCtrl { } } - this.xcMeta.audit(null, null, 'nc_audit', { op_type: 'AUTHENTICATION', op_sub_type: 'ROLES_MANAGEMENT', user: req.user.email, - description: `updated roles `, ip: req.clientIp - }) + description: `updated roles `, + ip: req.clientIp + }); - res.json({msg: 'success'}); + res.json({ msg: 'success' }); } catch (e) { - next(e) + next(e); } } // @ts-ignore protected async deleteRole(req, res, next): Promise { try { - const role = await this.xcMeta.metaGet('', '', NC_ROLES, {id: req.params.id}); + const role = await this.xcMeta.metaGet('', '', NC_ROLES, { + id: req.params.id + }); if (!role) { return next(new Error(`Role with id '${req.params.id}' not found`)); } @@ -1340,16 +1553,26 @@ export default class RestAuthCtrl { for (const builder of (this.app as Noco).getBuilders()) { try { await this.xcMeta.startTransaction(); - const aclRows = await this.xcMeta.metaList('', builder.getDbAlias(), NC_ACL); + const aclRows = await this.xcMeta.metaList( + '', + builder.getDbAlias(), + NC_ACL + ); for (const aclRow of aclRows) { if (aclRow.acl) { const acl = JSON.parse(aclRow.acl); delete acl[deleteRoleName]; - await this.xcMeta.metaUpdate('', builder.getDbAlias(), NC_ACL, { - acl: JSON.stringify(acl) - }, { - id: aclRow.id - }); + await this.xcMeta.metaUpdate( + '', + builder.getDbAlias(), + NC_ACL, + { + acl: JSON.stringify(acl) + }, + { + id: aclRow.id + } + ); } } await this.xcMeta.commit(); @@ -1358,8 +1581,8 @@ export default class RestAuthCtrl { } } - await this.xcMeta.metaDelete('', '', NC_ROLES, {id: req.params.id}); - res.json({msg: 'success'}) + await this.xcMeta.metaDelete('', '', NC_ROLES, { id: req.params.id }); + res.json({ msg: 'success' }); } catch (e) { next(e); } @@ -1376,7 +1599,7 @@ export default class RestAuthCtrl { /* Admin apis */ protected async createAuthTableIfNotExists(): Promise { if (!(await this.dbDriver.schema.hasTable('xc_users'))) { - await this.dbDriver.schema.createTable('xc_users', function (table) { + await this.dbDriver.schema.createTable('xc_users', function(table) { table.increments(); table.string('email'); table.string('password', 255); @@ -1393,7 +1616,7 @@ export default class RestAuthCtrl { table.boolean('email_verified'); table.string('roles', 255).defaultTo('editor'); table.timestamps(); - }) + }); } } @@ -1410,7 +1633,6 @@ export default class RestAuthCtrl { return crypto.randomBytes(40).toString('hex'); } - // protected async xcSendInviteEmail(reqBody: { // fromEmail: string; // toEmail: string; @@ -1423,12 +1645,10 @@ export default class RestAuthCtrl { // } // } - protected get emailClient(): IEmailAdapter { return this.app?.metaMgr?.emailAdapter; } - public async loadLatestApiTokens(): Promise { this.apiTokens = await this.xcMeta.metaList(null, null, 'nc_api_tokens'); } diff --git a/packages/nocodb/src/lib/noco/rest/RestAuthCtrlEE.ts b/packages/nocodb/src/lib/noco/rest/RestAuthCtrlEE.ts index d927db0d1e..8f34dedc5b 100644 --- a/packages/nocodb/src/lib/noco/rest/RestAuthCtrlEE.ts +++ b/packages/nocodb/src/lib/noco/rest/RestAuthCtrlEE.ts @@ -1,32 +1,33 @@ -import {Tele} from 'nc-help'; +import { Tele } from 'nc-help'; import passport from 'passport'; -import {Strategy} from 'passport-jwt'; -import {v4 as uuidv4} from 'uuid'; -import validator from "validator"; +import { Strategy } from 'passport-jwt'; +import { v4 as uuidv4 } from 'uuid'; +import validator from 'validator'; -import XcCache from "../plugins/adapters/cache/XcCache"; +import XcCache from '../plugins/adapters/cache/XcCache'; -import RestAuthCtrl from "./RestAuthCtrl"; +import RestAuthCtrl from './RestAuthCtrl'; export default class RestAuthCtrlEE extends RestAuthCtrl { - - protected async addAdmin(req, res, next): Promise { - const emails = (req.body.email || '').split(/\s*,\s*/).map(v => v.trim()); // check for invalid emails - const invalidEmails = emails.filter(v => !validator.isEmail(v)) + const invalidEmails = emails.filter(v => !validator.isEmail(v)); if (!emails.length) { return next(new Error('Invalid email address')); } if (invalidEmails.length) { - return next(new Error('Invalid email address : ' + invalidEmails.join(', '))); + return next( + new Error('Invalid email address : ' + invalidEmails.join(', ')) + ); } - // todo: handle roles which contains super - if (!req.session?.passport?.user?.roles?.owner && req.body.roles.indexOf('owner') > -1) { + if ( + !req.session?.passport?.user?.roles?.owner && + req.body.roles.indexOf('owner') > -1 + ) { return next(new Error('Insufficient privilege to add super admin role.')); } @@ -34,95 +35,122 @@ export default class RestAuthCtrlEE extends RestAuthCtrl { const error = []; for (const email of emails) { - // add user to project if user already exist - const user = await this.users.where({email}).first(); + const user = await this.users.where({ email }).first(); if (user) { - - await this.users.update({ - roles: 'user' - }).where({roles: 'user_new', email}); - - if (!await this.xcMeta.isUserHaveAccessToProject(req.body.project_id, user.id)) { - await this.xcMeta.projectAddUser(req.body.project_id, user.id, 'editor'); + await this.users + .update({ + roles: 'user' + }) + .where({ roles: 'user_new', email }); + + if ( + !(await this.xcMeta.isUserHaveAccessToProject( + req.body.project_id, + user.id + )) + ) { + await this.xcMeta.projectAddUser( + req.body.project_id, + user.id, + 'editor' + ); } this.xcMeta.audit(req.body.project_id, null, 'nc_audit', { op_type: 'AUTHENTICATION', op_sub_type: 'INVITE', user: req.user.email, - description: `invited ${email} to ${req.body.project_id} project `, ip: req.clientIp - }) + description: `invited ${email} to ${req.body.project_id} project `, + ip: req.clientIp + }); } else { try { // create new user with invite token await this.users.insert({ invite_token, - invite_token_expires: new Date(Date.now() + (24 * 60 * 60 * 1000)), + invite_token_expires: new Date(Date.now() + 24 * 60 * 60 * 1000), email, roles: 'user' }); - const {id} = await this.users.where({email}).first(); + const { id } = await this.users.where({ email }).first(); const count = await this.users.count('id').first(); // add user to project - await this.xcMeta.projectAddUser(req.body.project_id, id, req.body.roles); + await this.xcMeta.projectAddUser( + req.body.project_id, + id, + req.body.roles + ); - Tele.emit('evt', {evt_type: 'project:invite', count: count?.count}); + Tele.emit('evt', { evt_type: 'project:invite', count: count?.count }); this.xcMeta.audit(req.body.project_id, null, 'nc_audit', { op_type: 'AUTHENTICATION', op_sub_type: 'INVITE', user: req.user.email, - description: `invited ${email} to ${req.body.project_id} project `, ip: req.clientIp - }) + description: `invited ${email} to ${req.body.project_id} project `, + ip: req.clientIp + }); // in case of single user check for smtp failure // and send back token if failed - if (emails.length === 1 && !await this.sendInviteEmail(email, invite_token, req)) { - return res.json({invite_token, email}); + if ( + emails.length === 1 && + !(await this.sendInviteEmail(email, invite_token, req)) + ) { + return res.json({ invite_token, email }); } else { - this.sendInviteEmail(email, invite_token, req) + this.sendInviteEmail(email, invite_token, req); } } catch (e) { if (emails.length === 1) { return next(e); } else { - error.push({email, error: e.message}) + error.push({ email, error: e.message }); } } } - } if (emails.length === 1) { res.json({ msg: 'success' - }) + }); } else { - return res.json({invite_token, emails, error}); + return res.json({ invite_token, emails, error }); } } protected async updateAdmin(req, res, next): Promise { - if (!req?.body?.project_id) { return next(new Error('Missing project id in request body.')); } - if (req.session?.passport?.user?.roles?.owner && req.session?.passport?.user?.id === +req.params.id && req.body.roles.indexOf('owner') === -1) { - return next(new Error('Super admin can\'t remove Super role themselves')); + if ( + req.session?.passport?.user?.roles?.owner && + req.session?.passport?.user?.id === +req.params.id && + req.body.roles.indexOf('owner') === -1 + ) { + return next(new Error("Super admin can't remove Super role themselves")); } try { - const user = await this.users.where({ - id: req.params.id - }).first(); + const user = await this.users + .where({ + id: req.params.id + }) + .first(); if (!user) { return next(`User with id '${req.params.id}' doesn't exist`); } // todo: handle roles which contains super - if (!req.session?.passport?.user?.roles?.owner && req.body.roles.indexOf('owner') > -1) { - return next(new Error('Insufficient privilege to add super admin role.')); + if ( + !req.session?.passport?.user?.roles?.owner && + req.body.roles.indexOf('owner') > -1 + ) { + return next( + new Error('Insufficient privilege to add super admin role.') + ); } // await this.users.update({ @@ -131,77 +159,88 @@ export default class RestAuthCtrlEE extends RestAuthCtrl { // id: req.params.id // }); - - await this.xcMeta.metaUpdate(req?.body?.project_id, null, 'nc_projects_users', { - roles: req.body.roles - }, { - user_id: req.params.id, - // email: req.body.email - }); + await this.xcMeta.metaUpdate( + req?.body?.project_id, + null, + 'nc_projects_users', + { + roles: req.body.roles + }, + { + user_id: req.params.id + // email: req.body.email + } + ); XcCache.del(`${req.body.email}___${req?.body?.project_id}`); - this.xcMeta.audit(null, null, 'nc_audit', { op_type: 'AUTHENTICATION', op_sub_type: 'ROLES_MANAGEMENT', user: req.user.email, - description: `updated roles for ${user.email} with ${req.body.roles} `, ip: req.clientIp - }) + description: `updated roles for ${user.email} with ${req.body.roles} `, + ip: req.clientIp + }); res.json({ msg: 'User details updated successfully' - }) - + }); } catch (e) { next(e); } - } protected initJwtStrategy(): void { - passport.use(new Strategy({ - ...this.jwtOptions, - passReqToCallback: true - }, (req, jwtPayload, done) => { - - const keyVals = [jwtPayload?.email] - if (req.ncProjectId) { - keyVals.push(req.ncProjectId); - } - const key = keyVals.join('___'); - const cachedVal = XcCache.get(key); - if (cachedVal) { - return done(null, cachedVal); - } - - this.users.where({ - email: jwtPayload?.email - }).first().then(user => { - if (req.ncProjectId) { - this.xcMeta.metaGet(req.ncProjectId, null, 'nc_projects_users', { - user_id: user?.id - }).then(projectUser => { - user.roles = projectUser.roles; - XcCache.set(key, user); - done(null, user) - }) - - } else { - // const roles = projectUser?.roles ? JSON.parse(projectUser.roles) : {guest: true}; - if (user) { - XcCache.set(key, user); - return done(null, user); - } else { - return done(new Error('User not found')); + passport.use( + new Strategy( + { + ...this.jwtOptions, + passReqToCallback: true + }, + (req, jwtPayload, done) => { + const keyVals = [jwtPayload?.email]; + if (req.ncProjectId) { + keyVals.push(req.ncProjectId); + } + const key = keyVals.join('___'); + const cachedVal = XcCache.get(key); + if (cachedVal) { + return done(null, cachedVal); } + + this.users + .where({ + email: jwtPayload?.email + }) + .first() + .then(user => { + if (req.ncProjectId) { + this.xcMeta + .metaGet(req.ncProjectId, null, 'nc_projects_users', { + user_id: user?.id + }) + .then(projectUser => { + user.roles = projectUser.roles; + XcCache.set(key, user); + done(null, user); + }); + } else { + // const roles = projectUser?.roles ? JSON.parse(projectUser.roles) : {guest: true}; + if (user) { + XcCache.set(key, user); + return done(null, user); + } else { + return done(new Error('User not found')); + } + } + }) + .catch(err => { + return done(err); + }); } - }).catch(err => { - return done(err); - }) - })); + ) + ); } - } /** diff --git a/packages/nocodb/src/lib/noco/rest/RestBaseCtrl.ts b/packages/nocodb/src/lib/noco/rest/RestBaseCtrl.ts index 1c2bd71dd2..1e1940e089 100644 --- a/packages/nocodb/src/lib/noco/rest/RestBaseCtrl.ts +++ b/packages/nocodb/src/lib/noco/rest/RestBaseCtrl.ts @@ -1,17 +1,15 @@ -import {Handler, NextFunction, Request, Response, Router} from "express"; -import Handlebars from "handlebars"; +import { Handler, NextFunction, Request, Response, Router } from 'express'; +import Handlebars from 'handlebars'; -import {Route} from "../../../interface/config"; +import { Route } from '../../../interface/config'; export abstract class RestBaseCtrl { - public router: Router; public routes: Route[]; protected rootPath: string; protected middlewareBody: string; - public updateMiddleware(middlewareBody: string): this { this.middlewareBody = middlewareBody; return this; @@ -22,9 +20,10 @@ export abstract class RestBaseCtrl { return this; } - public mapRoutes(router: Router, customRoutes?: any): any { - const middleware = this.middlewareBody ? this.generateHandlerFromStringBody(this.middlewareBody) : this.middleware; + const middleware = this.middlewareBody + ? this.generateHandlerFromStringBody(this.middlewareBody) + : this.middleware; customRoutes?.additional?.[this.controllerName]?.forEach(addRoute => { const handlers = [middleware]; @@ -33,31 +32,50 @@ export abstract class RestBaseCtrl { handlers.push(this.postMiddleware); - router[addRoute.method](encodeURI(addRoute.path.slice(this.rootPath.length)), ...handlers); - - }) + router[addRoute.method]( + encodeURI(addRoute.path.slice(this.rootPath.length)), + ...handlers + ); + }); this.routes.forEach((route: Route) => { const handlers = [middleware]; if (customRoutes?.override?.[route.path]?.[route.type]?.length) { handlers.push( - ...customRoutes.override[route.path][route.type].map(hn => this.catchErr(hn)) + ...customRoutes.override[route.path][route.type].map(hn => + this.catchErr(hn) + ) + ); + } else if ( + route.functions && + Array.isArray(route.functions) && + route.functions.length + ) { + handlers.push( + ...route.functions.map(fnBody => { + return this.catchErr(this.generateHandlerFromStringBody(fnBody)); + }) ); - } else if (route.functions && Array.isArray(route.functions) && route.functions.length) { - handlers.push(...route.functions.map(fnBody => { - return this.catchErr(this.generateHandlerFromStringBody(fnBody)) - })); } else { - handlers.push(...route.handler.map(h => { - return this.catchErr((typeof h === 'string' ? (h in this ? this[h] : ((_req, res) => res.json({}))).bind(this) : h)); - })) + handlers.push( + ...route.handler.map(h => { + return this.catchErr( + typeof h === 'string' + ? (h in this ? this[h] : (_req, res) => res.json({})).bind(this) + : h + ); + }) + ); } handlers.push(this.postMiddleware); - router[route.type](encodeURI(route.path.slice(this.rootPath.length)), ...handlers); - }) + router[route.type]( + encodeURI(route.path.slice(this.rootPath.length)), + ...handlers + ); + }); } protected generateHandlerFromStringBody(fnBody: string): Handler { @@ -72,12 +90,11 @@ export abstract class RestBaseCtrl { // tslint:disable-next-line:no-eval handler = eval(js); } catch (e) { - console.log('Error in transpilation', e) + console.log('Error in transpilation', e); } return handler; } - protected generateMiddlewareFromStringBody(fnBody: string): Handler { // @ts-ignore let middleware = (_req: Request, res: Response, _next: NextFunction) => { @@ -88,37 +105,45 @@ export abstract class RestBaseCtrl { const js = `((${fnBody}).bind(this))`; // tslint:disable-next-line:no-eval middleware = eval(js); - } catch (e) { - console.log('Error in transpilation', e) + console.log('Error in transpilation', e); } return middleware; } protected catchErr(handler): Handler { return (req, res, next) => { - (res as any).xcJson = (data) => { + (res as any).xcJson = data => { res.locals.responseData = data; next(); - } + }; Promise.resolve(handler.call(this, req, res, next)).catch(err => { next(err); }); }; } - protected abstract postMiddleware(req: Request, res: Response, next: NextFunction): Promise; + protected abstract postMiddleware( + req: Request, + res: Response, + next: NextFunction + ): Promise; - protected abstract middleware(req: Request, res: Response, next: NextFunction): Promise; + protected abstract middleware( + req: Request, + res: Response, + next: NextFunction + ): Promise; abstract get controllerName(): string; protected replaceEnvVarRec(obj, req): any { - return JSON.parse(JSON.stringify(obj), (_key, value) => { - return typeof value === 'string' ? Handlebars.compile(value, {noEscape: true})({ - req - }) : value; + return typeof value === 'string' + ? Handlebars.compile(value, { noEscape: true })({ + req + }) + : value; }); } } diff --git a/packages/nocodb/src/lib/noco/rest/RestCtrl.ts b/packages/nocodb/src/lib/noco/rest/RestCtrl.ts index a41b5d2cef..c96b498d06 100644 --- a/packages/nocodb/src/lib/noco/rest/RestCtrl.ts +++ b/packages/nocodb/src/lib/noco/rest/RestCtrl.ts @@ -1,29 +1,32 @@ import autoBind from 'auto-bind'; -import {NextFunction, Request, Response, Router} from "express"; +import { NextFunction, Request, Response, Router } from 'express'; -import {Acl, Acls, Route} from "../../../interface/config"; -import {BaseModelSql} from "../../dataMapper"; - -import {RestBaseCtrl} from "./RestBaseCtrl"; +import { Acl, Acls, Route } from '../../../interface/config'; +import { BaseModelSql } from '../../dataMapper'; +import { RestBaseCtrl } from './RestBaseCtrl'; function parseHrtimeToSeconds(hrtime) { - const seconds = (hrtime[0] + (hrtime[1] / 1e6)).toFixed(3); + const seconds = (hrtime[0] + hrtime[1] / 1e6).toFixed(3); return seconds; } - export class RestCtrl extends RestBaseCtrl { - public app: any; - private table: string; private models: { [key: string]: BaseModelSql }; private acls: Acls; - - constructor(app: any, models: { [key: string]: BaseModelSql }, table: string, routes: Route[], rootPath: string, acls: Acls, middlewareBody?: string) { + constructor( + app: any, + models: { [key: string]: BaseModelSql }, + table: string, + routes: Route[], + rootPath: string, + acls: Acls, + middlewareBody?: string + ) { super(); autoBind(this); this.app = app; @@ -36,7 +39,6 @@ export class RestCtrl extends RestBaseCtrl { this.acls = acls; } - private get model(): BaseModelSql { return this.models?.[this.table]; } @@ -102,8 +104,14 @@ export class RestCtrl extends RestBaseCtrl { } public async count(req: Request | any, res: Response): Promise { - if (req.query.conditionGraph && typeof req.query.conditionGraph === 'string') { - req.query.conditionGraph = {models: this.models, condition: JSON.parse(req.query.conditionGraph)} + if ( + req.query.conditionGraph && + typeof req.query.conditionGraph === 'string' + ) { + req.query.conditionGraph = { + models: this.models, + condition: JSON.parse(req.query.conditionGraph) + }; } const data = await req.model.countByPk({ ...req.query @@ -125,7 +133,6 @@ export class RestCtrl extends RestBaseCtrl { res.json(data); } - public async bulkInsert(req: Request | any, res: Response): Promise { const data = await req.model.insertb(req.body); res.json(data); @@ -137,7 +144,7 @@ export class RestCtrl extends RestBaseCtrl { } public async bulkDelete(req: Request | any, res: Response): Promise { - const data = await req.model.delb(req.body) + const data = await req.model.delb(req.body); res.json(data); } @@ -145,13 +152,19 @@ export class RestCtrl extends RestBaseCtrl { const startTime = process.hrtime(); try { - if (req.query.conditionGraph && typeof req.query.conditionGraph === 'string') { - req.query.conditionGraph = {models: this.models, condition: JSON.parse(req.query.conditionGraph)} + if ( + req.query.conditionGraph && + typeof req.query.conditionGraph === 'string' + ) { + req.query.conditionGraph = { + models: this.models, + condition: JSON.parse(req.query.conditionGraph) + }; } if (req.query.condition && typeof req.query.condition === 'string') { - req.query.condition = JSON.parse(req.query.condition) + req.query.condition = JSON.parse(req.query.condition); } - }catch (e){ + } catch (e) { /* ignore parse error */ } @@ -167,13 +180,19 @@ export class RestCtrl extends RestBaseCtrl { const startTime = process.hrtime(); try { - if (req.query.conditionGraph && typeof req.query.conditionGraph === 'string') { - req.query.conditionGraph = {models: this.models, condition: JSON.parse(req.query.conditionGraph)} + if ( + req.query.conditionGraph && + typeof req.query.conditionGraph === 'string' + ) { + req.query.conditionGraph = { + models: this.models, + condition: JSON.parse(req.query.conditionGraph) + }; } if (req.query.condition && typeof req.query.condition === 'string') { - req.query.condition = JSON.parse(req.query.condition) + req.query.condition = JSON.parse(req.query.condition); } - }catch (e){ + } catch (e) { /* ignore parse error */ } @@ -185,12 +204,17 @@ export class RestCtrl extends RestBaseCtrl { res.xcJson(data); } - public async m2mNotChildren(req: Request | any, res): Promise { const startTime = process.hrtime(); - if (req.query.conditionGraph && typeof req.query.conditionGraph === 'string') { - req.query.conditionGraph = {models: this.models, condition: JSON.parse(req.query.conditionGraph)} + if ( + req.query.conditionGraph && + typeof req.query.conditionGraph === 'string' + ) { + req.query.conditionGraph = { + models: this.models, + condition: JSON.parse(req.query.conditionGraph) + }; } const list = await req.model.m2mNotChildren({ @@ -204,32 +228,39 @@ export class RestCtrl extends RestBaseCtrl { const elapsedSeconds = parseHrtimeToSeconds(process.hrtime(startTime)); res.setHeader('xc-db-response', elapsedSeconds); - res.xcJson({list, info: count}); + res.xcJson({ list, info: count }); } - protected async middleware(req: Request | any, res: Response, next: NextFunction): Promise { - + protected async middleware( + req: Request | any, + res: Response, + next: NextFunction + ): Promise { req.model = this.model; req.models = this.models; req.table = this.table; - const methodOperationMap = { get: 'read', post: 'create', put: 'update', - delete: 'delete', - } + delete: 'delete' + }; const roleOperationPossible = (roles, operation, object) => { const errors = []; for (const [roleName, isAllowed] of Object.entries(roles)) { - // todo: handle conditions from multiple roles if (this.acl?.[roleName]?.[operation]?.custom) { - const condition = this.replaceEnvVarRec(this.acl?.[roleName]?.[operation]?.custom, req); - (req as any).query.conditionGraph = {condition, models: this.models}; + const condition = this.replaceEnvVarRec( + this.acl?.[roleName]?.[operation]?.custom, + req + ); + (req as any).query.conditionGraph = { + condition, + models: this.models + }; } if (!isAllowed) { @@ -241,43 +272,48 @@ export class RestCtrl extends RestBaseCtrl { if (this.acl[roleName][operation]) { return true; } - } else if (this.acl?.[roleName]?.[operation] && roleOperationObjectGet(roleName, operation, object)) { + } else if ( + this.acl?.[roleName]?.[operation] && + roleOperationObjectGet(roleName, operation, object) + ) { return true; } } catch (e) { errors.push(e); } - } if (errors?.length) { - throw errors[0] + throw errors[0]; } return false; - } + }; // @ts-ignore const roleOperationObjectGet = (role, operation, object) => { - const columns = this.acl[role][operation].columns; if (columns) { // todo: merge allowed columns if multiple roles - const allowedCols = Object.keys(columns).filter(col => columns[col]) - res.locals.xcAcl = {allowedCols, operation, columns}; + const allowedCols = Object.keys(columns).filter(col => columns[col]); + res.locals.xcAcl = { allowedCols, operation, columns }; if (operation === 'update' || operation === 'create') { if (Array.isArray(object)) { for (const row of object) { for (const colInReq of Object.keys(row)) { if (!allowedCols.includes(colInReq)) { - throw new Error(`User doesn't have permission to edit '${colInReq}' column`); + throw new Error( + `User doesn't have permission to edit '${colInReq}' column` + ); } } } } else { for (const colInReq of Object.keys(object)) { if (!allowedCols.includes(colInReq)) { - throw new Error(`User doesn't have permission to edit '${colInReq}' column`); + throw new Error( + `User doesn't have permission to edit '${colInReq}' column` + ); } } } @@ -286,21 +322,27 @@ export class RestCtrl extends RestBaseCtrl { return Object.values(columns).some(Boolean); } } - } - - const roles = (req as any)?.locals?.user?.roles ?? (req as any)?.session?.passport?.user?.roles ?? { - guest: true }; + const roles = (req as any)?.locals?.user?.roles ?? + (req as any)?.session?.passport?.user?.roles ?? { + guest: true + }; try { - const allowed = roleOperationPossible(roles, methodOperationMap[req.method.toLowerCase()], req.body); + const allowed = roleOperationPossible( + roles, + methodOperationMap[req.method.toLowerCase()], + req.body + ); if (allowed) { // any additional rules can be made here return next(); } else { - const msg = roles.guest ? `Access Denied : Please Login or Signup for a new account` : `Access Denied for this account`; + const msg = roles.guest + ? `Access Denied : Please Login or Signup for a new account` + : `Access Denied for this account`; return res.status(403).json({ msg }); @@ -312,9 +354,11 @@ export class RestCtrl extends RestBaseCtrl { } } - - protected async postMiddleware(_req: Request, res: Response, _next: NextFunction): Promise { - + protected async postMiddleware( + _req: Request, + res: Response, + _next: NextFunction + ): Promise { const data = res.locals.responseData; if (!res.locals.xcAcl) { @@ -322,7 +366,7 @@ export class RestCtrl extends RestBaseCtrl { } // @ts-ignore - const {allowedCols, operation, columns} = res.locals.xcAcl; + const { allowedCols, operation, columns } = res.locals.xcAcl; if (Array.isArray(data)) { for (const row of data) { @@ -342,11 +386,9 @@ export class RestCtrl extends RestBaseCtrl { return res.json(data); } - get controllerName(): string { return this.table; } - } /** diff --git a/packages/nocodb/src/lib/noco/rest/RestCtrlBelongsTo.ts b/packages/nocodb/src/lib/noco/rest/RestCtrlBelongsTo.ts index aa810a42ba..276435b2ec 100644 --- a/packages/nocodb/src/lib/noco/rest/RestCtrlBelongsTo.ts +++ b/packages/nocodb/src/lib/noco/rest/RestCtrlBelongsTo.ts @@ -1,25 +1,31 @@ import autoBind from 'auto-bind'; -import {NextFunction, Request, Response} from "express"; +import { NextFunction, Request, Response } from 'express'; -import {Acl, Acls, Route} from "../../../interface/config"; -import {BaseModelSql} from "../../dataMapper"; +import { Acl, Acls, Route } from '../../../interface/config'; +import { BaseModelSql } from '../../dataMapper'; - -import {RestBaseCtrl} from "./RestBaseCtrl"; +import { RestBaseCtrl } from './RestBaseCtrl'; export class RestCtrlBelongsTo extends RestBaseCtrl { - public parentTable: string; public childTable: string; - public app: any; // public routes: Route[]; private models: { [key: string]: BaseModelSql }; private acls: Acls; - constructor(app: any, models: { [key: string]: BaseModelSql }, parentTable: string, childTable: string, routes: Route[], rootPath: string, acls: Acls, middlewareBody?: string) { + constructor( + app: any, + models: { [key: string]: BaseModelSql }, + parentTable: string, + childTable: string, + routes: Route[], + rootPath: string, + acls: Acls, + middlewareBody?: string + ) { super(); autoBind(this); this.app = app; @@ -42,7 +48,6 @@ export class RestCtrlBelongsTo extends RestBaseCtrl { return this.models?.[this.childTable]; } - private get parentAcl(): Acl { return this.acls?.[this.parentTable]; } @@ -59,7 +64,11 @@ export class RestCtrlBelongsTo extends RestBaseCtrl { res.xcJson(data); } - protected async middleware(req: Request | any, res: Response, next: NextFunction): Promise { + protected async middleware( + req: Request | any, + res: Response, + next: NextFunction + ): Promise { req.childModel = this.childModel; req.parentModel = this.parentModel; req.parentTable = this.parentTable; @@ -69,31 +78,45 @@ export class RestCtrlBelongsTo extends RestBaseCtrl { get: 'read', post: 'create', put: 'update', - delete: 'delete', - } + delete: 'delete' + }; const roleOperationPossible = (roles, operation, object) => { const errors = []; - res.locals.xcAcl = {operation}; + res.locals.xcAcl = { operation }; for (const [roleName, isAllowed] of Object.entries(roles)) { - // todo: handling conditions from multiple roles if (this.childAcl?.[roleName]?.[operation]?.custom) { // req.query.condition = replaceEnvVarRec(this.acl?.[roleName]?.[operation]?.custom) - const condition = this.replaceEnvVarRec(this.childAcl?.[roleName]?.[operation]?.custom,req); - (req as any).query.childNestedCondition = {condition, models: this.models}; + const condition = this.replaceEnvVarRec( + this.childAcl?.[roleName]?.[operation]?.custom, + req + ); + (req as any).query.childNestedCondition = { + condition, + models: this.models + }; } if (this.parentAcl?.[roleName]?.[operation]?.custom) { // req.query.condition = replaceEnvVarRec(this.acl?.[roleName]?.[operation]?.custom) - const condition = this.replaceEnvVarRec(this.parentAcl?.[roleName]?.[operation]?.custom,req); - (req as any).query.conditionGraph = {condition, models: this.models}; + const condition = this.replaceEnvVarRec( + this.parentAcl?.[roleName]?.[operation]?.custom, + req + ); + (req as any).query.conditionGraph = { + condition, + models: this.models + }; } const childColumns = this.childAcl[roleName]?.[operation]?.columns; if (childColumns) { - const allowedChildCols = Object.keys(childColumns).filter(col => childColumns[col]); - res.locals.xcAcl.allowedChildCols = res.locals.xcAcl.allowedChildCols || []; + const allowedChildCols = Object.keys(childColumns).filter( + col => childColumns[col] + ); + res.locals.xcAcl.allowedChildCols = + res.locals.xcAcl.allowedChildCols || []; res.locals.xcAcl.allowedChildCols.push(...allowedChildCols); res.locals.xcAcl.childColumns = childColumns; } @@ -107,7 +130,10 @@ export class RestCtrlBelongsTo extends RestBaseCtrl { if (this.parentAcl[roleName][operation]) { return true; } - } else if (this.parentAcl?.[roleName]?.[operation] && roleOperationObjectGet(roleName, operation, object)) { + } else if ( + this.parentAcl?.[roleName]?.[operation] && + roleOperationObjectGet(roleName, operation, object) + ) { return true; } } catch (e) { @@ -115,10 +141,10 @@ export class RestCtrlBelongsTo extends RestBaseCtrl { } } if (errors?.length) { - throw errors[0] + throw errors[0]; } return false; - } + }; // @ts-ignore const roleOperationObjectGet = (role, operation, object) => { @@ -126,28 +152,38 @@ export class RestCtrlBelongsTo extends RestBaseCtrl { if (columns) { // todo: merge allowed columns if multiple roles - const allowedParentCols = Object.keys(columns).filter(col => columns[col]); - Object.assign(res.locals.xcAcl, {allowedParentCols, parentColumns: columns}) + const allowedParentCols = Object.keys(columns).filter( + col => columns[col] + ); + Object.assign(res.locals.xcAcl, { + allowedParentCols, + parentColumns: columns + }); return Object.values(columns).some(Boolean); } - } - + }; - console.log(`${this.parentModel.tn}Hm${this.childModel.tn} middleware`) + console.log(`${this.parentModel.tn}Hm${this.childModel.tn} middleware`); - - const roles = (req as any)?.locals?.user?.roles ?? (req as any)?.session?.passport?.user?.roles ?? { - guest: true - }; + const roles = (req as any)?.locals?.user?.roles ?? + (req as any)?.session?.passport?.user?.roles ?? { + guest: true + }; try { - const allowed = roleOperationPossible(roles, methodOperationMap[req.method.toLowerCase()], req.body); + const allowed = roleOperationPossible( + roles, + methodOperationMap[req.method.toLowerCase()], + req.body + ); if (allowed) { // any additional rules can be made here return next(); } else { - const msg = roles.guest ? `Access Denied : Please Login or Signup for a new account` : `Access Denied for this account`; + const msg = roles.guest + ? `Access Denied : Please Login or Signup for a new account` + : `Access Denied for this account`; return res.status(403).json({ msg }); @@ -159,20 +195,28 @@ export class RestCtrlBelongsTo extends RestBaseCtrl { } } - - protected async postMiddleware(req: Request, res: Response, _next: NextFunction): Promise { - - + protected async postMiddleware( + req: Request, + res: Response, + _next: NextFunction + ): Promise { const data = res.locals.responseData; if (!res.locals.xcAcl) { return res.json(data); } // @ts-ignore - const {allowedChildCols, operation, allowedParentCols, parentColumns, childColumns} = res.locals.xcAcl; - - const isBt = req.url.toLowerCase().startsWith('/belongs/' + this.parentTable.toLowerCase()); - + const { + allowedChildCols, + operation, + allowedParentCols, + parentColumns, + childColumns + } = res.locals.xcAcl; + + const isBt = req.url + .toLowerCase() + .startsWith('/belongs/' + this.parentTable.toLowerCase()); if ((!allowedChildCols || !isBt) && !allowedParentCols) { return res.json(data); @@ -191,10 +235,18 @@ export class RestCtrlBelongsTo extends RestBaseCtrl { delete row[colInRes][colInChild]; } } - } else if (childColumns && colInRes in childColumns && !childColumns[colInRes]) { + } else if ( + childColumns && + colInRes in childColumns && + !childColumns[colInRes] + ) { delete row[colInRes]; } - } else if (childColumns && colInRes in childColumns && !childColumns[colInRes]) { + } else if ( + childColumns && + colInRes in childColumns && + !childColumns[colInRes] + ) { delete row[colInRes]; } } @@ -209,7 +261,6 @@ export class RestCtrlBelongsTo extends RestBaseCtrl { return res.json(data); } - get controllerName(): string { return `${this.childTable}.bt.${this.parentTable}`; } diff --git a/packages/nocodb/src/lib/noco/rest/RestCtrlCustom.ts b/packages/nocodb/src/lib/noco/rest/RestCtrlCustom.ts index ba5fbe69e3..b591207094 100644 --- a/packages/nocodb/src/lib/noco/rest/RestCtrlCustom.ts +++ b/packages/nocodb/src/lib/noco/rest/RestCtrlCustom.ts @@ -1,20 +1,22 @@ import autoBind from 'auto-bind'; -import {NextFunction, Request, Response, Router} from "express"; +import { NextFunction, Request, Response, Router } from 'express'; -import {Route} from "../../../interface/config"; -import {BaseModelSql} from "../../dataMapper"; +import { Route } from '../../../interface/config'; +import { BaseModelSql } from '../../dataMapper'; -import {RestBaseCtrl} from "./RestBaseCtrl"; +import { RestBaseCtrl } from './RestBaseCtrl'; export class RestCtrlCustom extends RestBaseCtrl { - public app: any; - protected models: { [key: string]: BaseModelSql }; - - constructor(app: any, models: { [key: string]: BaseModelSql }, routes: Route[], middlewareBody?: string) { + constructor( + app: any, + models: { [key: string]: BaseModelSql }, + routes: Route[], + middlewareBody?: string + ) { super(); autoBind(this); this.app = app; @@ -25,18 +27,25 @@ export class RestCtrlCustom extends RestBaseCtrl { this.rootPath = ''; } - protected async middleware(_req: Request, _res: Response, next: NextFunction): Promise { + protected async middleware( + _req: Request, + _res: Response, + next: NextFunction + ): Promise { next(); // return Promise.resolve(undefined); } - protected postMiddleware(_req: Request, _res: Response, _next: NextFunction): Promise { + protected postMiddleware( + _req: Request, + _res: Response, + _next: NextFunction + ): Promise { return Promise.resolve(undefined); } - get controllerName(): string { - return "__xc_custom"; + return '__xc_custom'; } } /** diff --git a/packages/nocodb/src/lib/noco/rest/RestCtrlHasMany.ts b/packages/nocodb/src/lib/noco/rest/RestCtrlHasMany.ts index 133a1448ec..d58bc4e7f3 100644 --- a/packages/nocodb/src/lib/noco/rest/RestCtrlHasMany.ts +++ b/packages/nocodb/src/lib/noco/rest/RestCtrlHasMany.ts @@ -1,24 +1,30 @@ import autoBind from 'auto-bind'; -import {NextFunction, Request, Response} from "express"; +import { NextFunction, Request, Response } from 'express'; -import {Acl, Acls, Route} from "../../../interface/config"; -import {BaseModelSql} from "../../dataMapper"; +import { Acl, Acls, Route } from '../../../interface/config'; +import { BaseModelSql } from '../../dataMapper'; - -import {RestBaseCtrl} from "./RestBaseCtrl"; +import { RestBaseCtrl } from './RestBaseCtrl'; export class RestCtrlHasMany extends RestBaseCtrl { - public parentTable: string; public childTable: string; - public app: any; // public routes: Route[]; private models: { [key: string]: BaseModelSql }; private acls: Acls; - constructor(app: any, models: { [key: string]: BaseModelSql }, parentTable: string, childTable: string, routes: Route[], rootPath: string, acls: Acls, middlewareBody?: string) { + constructor( + app: any, + models: { [key: string]: BaseModelSql }, + parentTable: string, + childTable: string, + routes: Route[], + rootPath: string, + acls: Acls, + middlewareBody?: string + ) { super(); autoBind(this); this.app = app; @@ -114,13 +120,12 @@ export class RestCtrlHasMany extends RestBaseCtrl { res.json(data); } - public async list(req: Request | any, res): Promise { const data = await req.parentModel.hasManyChildren({ child: req.childModel.tn, ...req.params, ...req.query - } as any) + } as any); res.xcJson(data); } @@ -132,8 +137,11 @@ export class RestCtrlHasMany extends RestBaseCtrl { res.xcJson(data); } - protected async middleware(req: Request | any, res: Response, next: NextFunction): Promise { - + protected async middleware( + req: Request | any, + res: Response, + next: NextFunction + ): Promise { req.childModel = this.childModel; req.parentModel = this.parentModel; req.parentTable = this.parentTable; @@ -143,30 +151,43 @@ export class RestCtrlHasMany extends RestBaseCtrl { get: 'read', post: 'create', put: 'update', - delete: 'delete', - } - + delete: 'delete' + }; const roleOperationPossible = (roles, operation, object) => { const errors = []; - res.locals.xcAcl = {operation}; + res.locals.xcAcl = { operation }; for (const [roleName, isAllowed] of Object.entries(roles)) { - if (this.childAcl?.[roleName]?.[operation]?.custom) { - const condition = this.replaceEnvVarRec(this.childAcl?.[roleName]?.[operation]?.custom, req); - (req as any).query.conditionGraph = {condition, models: this.models}; + const condition = this.replaceEnvVarRec( + this.childAcl?.[roleName]?.[operation]?.custom, + req + ); + (req as any).query.conditionGraph = { + condition, + models: this.models + }; } if (this.parentAcl?.[roleName]?.[operation]?.custom) { - const condition = this.replaceEnvVarRec(this.parentAcl?.[roleName]?.[operation]?.custom, req); - (req as any).query.parentNestedCondition = {condition, models: this.models}; + const condition = this.replaceEnvVarRec( + this.parentAcl?.[roleName]?.[operation]?.custom, + req + ); + (req as any).query.parentNestedCondition = { + condition, + models: this.models + }; } const parentColumns = this.parentAcl?.[roleName]?.[operation]?.columns; if (parentColumns) { - const allowedParentCols = Object.keys(parentColumns).filter(col => parentColumns[col]); - res.locals.xcAcl.allowedParentCols = res.locals.xcAcl.allowedParentCols || []; + const allowedParentCols = Object.keys(parentColumns).filter( + col => parentColumns[col] + ); + res.locals.xcAcl.allowedParentCols = + res.locals.xcAcl.allowedParentCols || []; res.locals.xcAcl.allowedParentCols.push(...allowedParentCols); // todo: merge columns @@ -182,7 +203,10 @@ export class RestCtrlHasMany extends RestBaseCtrl { if (this.childAcl?.[roleName]?.[operation]) { return true; } - } else if (this.childAcl?.[roleName]?.[operation] && roleOperationObjectGet(roleName, operation, object)) { + } else if ( + this.childAcl?.[roleName]?.[operation] && + roleOperationObjectGet(roleName, operation, object) + ) { return true; } } catch (e) { @@ -190,10 +214,10 @@ export class RestCtrlHasMany extends RestBaseCtrl { } } if (errors?.length) { - throw errors[0] + throw errors[0]; } return false; - } + }; // @ts-ignore const roleOperationObjectGet = (role, operation, object) => { @@ -201,22 +225,31 @@ export class RestCtrlHasMany extends RestBaseCtrl { if (columns) { // todo: merge allowed columns if multiple roles - const allowedChildCols = Object.keys(columns).filter(col => columns[col]); - Object.assign(res.locals.xcAcl, {allowedChildCols, childColumns: columns}) + const allowedChildCols = Object.keys(columns).filter( + col => columns[col] + ); + Object.assign(res.locals.xcAcl, { + allowedChildCols, + childColumns: columns + }); if (operation === 'update' || operation === 'create') { if (Array.isArray(object)) { for (const row of object) { for (const colInReq of Object.keys(row)) { if (!allowedChildCols.includes(colInReq)) { - throw new Error(`User doesn't have permission to edit '${colInReq}' column`); + throw new Error( + `User doesn't have permission to edit '${colInReq}' column` + ); } } } } else { for (const colInReq of Object.keys(object)) { if (!allowedChildCols.includes(colInReq)) { - throw new Error(`User doesn't have permission to edit '${colInReq}' column`); + throw new Error( + `User doesn't have permission to edit '${colInReq}' column` + ); } } } @@ -225,23 +258,28 @@ export class RestCtrlHasMany extends RestBaseCtrl { return Object.values(columns).some(Boolean); } } - } - - - console.log(`${this.parentModel.tn}Hm${this.childModel.tn} middleware`) + }; + console.log(`${this.parentModel.tn}Hm${this.childModel.tn} middleware`); - const roles = (req as any)?.locals?.user?.roles ?? (req as any)?.session?.passport?.user?.roles ?? { - guest: true - }; + const roles = (req as any)?.locals?.user?.roles ?? + (req as any)?.session?.passport?.user?.roles ?? { + guest: true + }; try { - const allowed = roleOperationPossible(roles, methodOperationMap[req.method.toLowerCase()], req.body); + const allowed = roleOperationPossible( + roles, + methodOperationMap[req.method.toLowerCase()], + req.body + ); if (allowed) { // any additional rules can be made here return next(); } else { - const msg = roles.guest ? `Access Denied : Please Login or Signup for a new account` : `Access Denied for this account`; + const msg = roles.guest + ? `Access Denied : Please Login or Signup for a new account` + : `Access Denied for this account`; return res.status(403).json({ msg }); @@ -253,20 +291,22 @@ export class RestCtrlHasMany extends RestBaseCtrl { } } - - protected async postMiddleware(req: Request, res: Response, _next: NextFunction): Promise { - - + protected async postMiddleware( + req: Request, + res: Response, + _next: NextFunction + ): Promise { const data = res.locals.responseData; if (!res.locals.xcAcl) { return res.json(data); } // @ts-ignore - const {operation, parentColumns, childColumns} = res.locals.xcAcl; - - const isHm = req.url.toLowerCase().startsWith('/has/' + this.childTable.toLowerCase()); + const { operation, parentColumns, childColumns } = res.locals.xcAcl; + const isHm = req.url + .toLowerCase() + .startsWith('/has/' + this.childTable.toLowerCase()); if ((!parentColumns || !isHm) && !childColumns) { return res.json(data); @@ -287,10 +327,18 @@ export class RestCtrlHasMany extends RestBaseCtrl { } } } - } else if (parentColumns && colInRes in parentColumns && !parentColumns[colInRes]) { + } else if ( + parentColumns && + colInRes in parentColumns && + !parentColumns[colInRes] + ) { delete row[colInRes]; } - } else if (childColumns && colInRes in childColumns && !childColumns[colInRes]) { + } else if ( + childColumns && + colInRes in childColumns && + !childColumns[colInRes] + ) { delete row[colInRes]; } } @@ -306,11 +354,9 @@ export class RestCtrlHasMany extends RestBaseCtrl { return res.json(data); } - get controllerName(): string { return `${this.parentTable}.hm.${this.childTable}`; } - } /** * @copyright Copyright (c) 2021, Xgene Cloud Ltd diff --git a/packages/nocodb/src/lib/noco/rest/RestCtrlMin.ts b/packages/nocodb/src/lib/noco/rest/RestCtrlMin.ts index c3c1673d02..0acdabf7d1 100644 --- a/packages/nocodb/src/lib/noco/rest/RestCtrlMin.ts +++ b/packages/nocodb/src/lib/noco/rest/RestCtrlMin.ts @@ -1,24 +1,20 @@ import autoBind from 'auto-bind'; -import {Request, Response, Router} from "express"; - -import {Acls} from "../../../interface/config"; -import {BaseModelSql} from "../../dataMapper"; +import { Request, Response, Router } from 'express'; +import { Acls } from '../../../interface/config'; +import { BaseModelSql } from '../../dataMapper'; function parseHrtimeToSeconds(hrtime) { - const seconds = (hrtime[0] + (hrtime[1] / 1e6)).toFixed(3); + const seconds = (hrtime[0] + hrtime[1] / 1e6).toFixed(3); return seconds; } - export class RestCtrlMin { - public app: any; private models: { [key: string]: BaseModelSql }; // @ts-ignore private acls: Acls; - constructor(app: any, models: { [key: string]: BaseModelSql }, acls: Acls) { autoBind(this); this.app = app; @@ -26,7 +22,6 @@ export class RestCtrlMin { this.acls = acls; } - public async list(req: Request | any, res): Promise { const startTime = process.hrtime(); @@ -53,7 +48,7 @@ export class RestCtrlMin { public async delete(req: Request | any, res): Promise { const data = await req.model.delByPk(req.params.id, null, req); - this.app.xcMeta.meta + this.app.xcMeta.meta; res.xcJson(data); } @@ -104,7 +99,6 @@ export class RestCtrlMin { res.json(data); } - public async bulkInsert(req: Request | any, res: Response): Promise { const data = await req.model.insertb(req.body); res.json(data); @@ -116,7 +110,7 @@ export class RestCtrlMin { } public async bulkDelete(req: Request | any, res: Response): Promise { - const data = await req.model.delb(req.body) + const data = await req.model.delb(req.body); res.json(data); } @@ -256,16 +250,19 @@ export class RestCtrlMin { } */ - public mapRoutes(router: Router): any { - router.get('/api/v2/:_tn', (req: any, res: any, next) => { - req.model = Object.values(this.models).find(m => m._tn === req.params._tn) - res.xcJson = res.json - next(); - }, this.list) + router.get( + '/api/v2/:_tn', + (req: any, res: any, next) => { + req.model = Object.values(this.models).find( + m => m._tn === req.params._tn + ); + res.xcJson = res.json; + next(); + }, + this.list + ); } - - } /** diff --git a/packages/nocodb/src/lib/noco/rest/RestCtrlProcedure.ts b/packages/nocodb/src/lib/noco/rest/RestCtrlProcedure.ts index 8d0b2b13bf..e9e411346d 100644 --- a/packages/nocodb/src/lib/noco/rest/RestCtrlProcedure.ts +++ b/packages/nocodb/src/lib/noco/rest/RestCtrlProcedure.ts @@ -1,17 +1,20 @@ import autoBind from 'auto-bind'; -import {Handler, NextFunction, Request, Response, Router} from "express"; +import { Handler, NextFunction, Request, Response, Router } from 'express'; -import BaseProcedure from "../common/BaseProcedure"; -import XcProcedure from "../common/XcProcedure"; +import BaseProcedure from '../common/BaseProcedure'; +import XcProcedure from '../common/XcProcedure'; -import {RestApiBuilder} from "./RestApiBuilder"; - -export class RestCtrlProcedure - extends BaseProcedure { +import { RestApiBuilder } from './RestApiBuilder'; +export class RestCtrlProcedure extends BaseProcedure { private acls: { [aclName: string]: { [role: string]: boolean } }; - constructor(builder: RestApiBuilder, functions: any[], procedures: any[], acls: { [p: string]: { [p: string]: boolean } }) { + constructor( + builder: RestApiBuilder, + functions: any[], + procedures: any[], + acls: { [p: string]: { [p: string]: boolean } } + ) { super(); autoBind(this); this.builder = builder; @@ -21,15 +24,14 @@ export class RestCtrlProcedure this.xcProcedure = new XcProcedure(builder); } - public fnHandler(name) { return (async (req, res, next) => { try { res.json(await this.xcProcedure.callFunction(name, req.body)); } catch (e) { - next(e) + next(e); } - }).bind(this) + }).bind(this); } private procHandler(name) { @@ -37,52 +39,84 @@ export class RestCtrlProcedure try { res.json(await this.xcProcedure.callProcedure(name, req.body)); } catch (e) { - next(e) + next(e); } - }).bind(this) + }).bind(this); } public mapRoutes(router: Router, customRoutes: any): any { // todo: load routers based on procedure/function list if (this.functions) { - for (const {function_name} of this.functions) { + for (const { function_name } of this.functions) { const routePath = '/_function/' + function_name; - if (customRoutes?.[`/api/${this.builder.routeVersionLetter}${routePath}`]?.post?.length) { - router.post(routePath, this.middleware(function_name), ...(customRoutes[`/api/${this.builder.routeVersionLetter}${routePath}`].post)) + if ( + customRoutes?.[`/api/${this.builder.routeVersionLetter}${routePath}`] + ?.post?.length + ) { + router.post( + routePath, + this.middleware(function_name), + ...customRoutes[ + `/api/${this.builder.routeVersionLetter}${routePath}` + ].post + ); } else { - router.post(routePath, this.middleware(function_name), this.fnHandler(function_name)) + router.post( + routePath, + this.middleware(function_name), + this.fnHandler(function_name) + ); } } } if (this.procedures) { - for (const {procedure_name} of this.procedures) { + for (const { procedure_name } of this.procedures) { const routePath = '/_procedure/' + procedure_name; - if (customRoutes?.[`/api/${this.builder.routeVersionLetter}${routePath}`]?.post?.length) { - router.post(routePath, this.middleware(procedure_name), ...(customRoutes[`/api/${this.builder.routeVersionLetter}${routePath}`].post)) + if ( + customRoutes?.[`/api/${this.builder.routeVersionLetter}${routePath}`] + ?.post?.length + ) { + router.post( + routePath, + this.middleware(procedure_name), + ...customRoutes[ + `/api/${this.builder.routeVersionLetter}${routePath}` + ].post + ); } else { - router.post(routePath, this.middleware(procedure_name), this.procHandler(procedure_name)) + router.post( + routePath, + this.middleware(procedure_name), + this.procHandler(procedure_name) + ); } } } } - private middleware(name: string): Handler { - return (async (req: Request, res: Response, next: NextFunction): Promise => { - - - const roles = (req as any)?.locals?.user?.roles ?? (req as any)?.session?.passport?.user?.roles ?? { - guest: true - }; + return (async ( + req: Request, + res: Response, + next: NextFunction + ): Promise => { + const roles = (req as any)?.locals?.user?.roles ?? + (req as any)?.session?.passport?.user?.roles ?? { + guest: true + }; try { - const allowed = Object.keys(roles).some(role => roles[role] && this.acls?.[name]?.[role]); + const allowed = Object.keys(roles).some( + role => roles[role] && this.acls?.[name]?.[role] + ); if (allowed) { // any additional rules can be made here return next(); } else { - const msg = roles.guest ? `Access Denied : Please Login or Signup for a new account` : `Access Denied for this account`; + const msg = roles.guest + ? `Access Denied : Please Login or Signup for a new account` + : `Access Denied for this account`; return res.status(403).json({ msg }); @@ -92,99 +126,89 @@ export class RestCtrlProcedure msg: e.message }); } - }).bind(this) + }).bind(this); } - - updateMiddleware(_body: string) { - - } + updateMiddleware(_body: string) {} public getSwaggerObj() { const swggerObj = { - "tags": [ + tags: [ { - "name": `Procedures`, - "description": `` + name: `Procedures`, + description: `` }, { - "name": `Functions`, - "description": `` + name: `Functions`, + description: `` } ], - "paths": {}, - } - + paths: {} + }; if (this.functions) { - for (const {function_name} of this.functions) { - swggerObj.paths[`/api/${this.builder.routeVersionLetter}/_function/${function_name}`] = { + for (const { function_name } of this.functions) { + swggerObj.paths[ + `/api/${this.builder.routeVersionLetter}/_function/${function_name}` + ] = { post: { tags: [`Functions`], - "summary": ``, - "description": "", - "operationId": `_function_${function_name}`, - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "parameters": [ + summary: ``, + description: '', + operationId: `_function_${function_name}`, + consumes: ['application/json'], + produces: ['application/json'], + parameters: [ { - "in": "body", - "name": "body", - "description": `Array of function arguments`, - "required": true + in: 'body', + name: 'body', + description: `Array of function arguments`, + required: true } ], responses: { - "200": { - description: "", - type: "object" + '200': { + description: '', + type: 'object' } } } - } + }; } } if (this.procedures) { - for (const {procedure_name} of this.procedures) { - swggerObj.paths[`/api/${this.builder.routeVersionLetter}/_procedure/${procedure_name}`] = { + for (const { procedure_name } of this.procedures) { + swggerObj.paths[ + `/api/${this.builder.routeVersionLetter}/_procedure/${procedure_name}` + ] = { post: { tags: [`Procedures`], - "summary": ``, - "description": "", - "operationId": `_procedure_${procedure_name}`, - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "parameters": [ + summary: ``, + description: '', + operationId: `_procedure_${procedure_name}`, + consumes: ['application/json'], + produces: ['application/json'], + parameters: [ { - "in": "body", - "name": "body", - "description": `Array of procedure arguments`, - "required": true + in: 'body', + name: 'body', + description: `Array of procedure arguments`, + required: true } ], responses: { - "200": { - description: "", - type: "object" + '200': { + description: '', + type: 'object' } } } - } + }; } } - return swggerObj; } - } /** * @copyright Copyright (c) 2021, Xgene Cloud Ltd diff --git a/packages/nocodb/src/lib/noco/rest/ui/auth/emailVerify.ts b/packages/nocodb/src/lib/noco/rest/ui/auth/emailVerify.ts index a2219f80b5..92b17ecd84 100644 --- a/packages/nocodb/src/lib/noco/rest/ui/auth/emailVerify.ts +++ b/packages/nocodb/src/lib/noco/rest/ui/auth/emailVerify.ts @@ -68,7 +68,8 @@ export default ` }) -`/** +`; +/** * @copyright Copyright (c) 2021, Xgene Cloud Ltd * * @author Naveen MR diff --git a/packages/nocodb/src/lib/noco/rest/ui/auth/resetPassword.ts b/packages/nocodb/src/lib/noco/rest/ui/auth/resetPassword.ts index 6ab1ed3790..1cbddcce66 100644 --- a/packages/nocodb/src/lib/noco/rest/ui/auth/resetPassword.ts +++ b/packages/nocodb/src/lib/noco/rest/ui/auth/resetPassword.ts @@ -102,7 +102,8 @@ export default ` }) -`/** +`; +/** * @copyright Copyright (c) 2021, Xgene Cloud Ltd * * @author Naveen MR diff --git a/packages/nocodb/src/lib/noco/rest/ui/auth/signin.ts b/packages/nocodb/src/lib/noco/rest/ui/auth/signin.ts index 983fe6f551..f315536afd 100644 --- a/packages/nocodb/src/lib/noco/rest/ui/auth/signin.ts +++ b/packages/nocodb/src/lib/noco/rest/ui/auth/signin.ts @@ -1,4 +1,4 @@ -export default ` +export default ` Welcome to Vue @@ -94,7 +94,8 @@ export default ` }) -`/** +`; +/** * @copyright Copyright (c) 2021, Xgene Cloud Ltd * * @author Naveen MR diff --git a/packages/nocodb/src/lib/noco/rest/ui/auth/signup.ts b/packages/nocodb/src/lib/noco/rest/ui/auth/signup.ts index 855f23f26b..865f2e571b 100644 --- a/packages/nocodb/src/lib/noco/rest/ui/auth/signup.ts +++ b/packages/nocodb/src/lib/noco/rest/ui/auth/signup.ts @@ -110,7 +110,8 @@ export default ` }) -`/** +`; +/** * @copyright Copyright (c) 2021, Xgene Cloud Ltd * * @author Naveen MR diff --git a/packages/nocodb/src/lib/noco/rest/ui/auth/swagger.ts b/packages/nocodb/src/lib/noco/rest/ui/auth/swagger.ts index d8e5fe666d..c2872a8643 100644 --- a/packages/nocodb/src/lib/noco/rest/ui/auth/swagger.ts +++ b/packages/nocodb/src/lib/noco/rest/ui/auth/swagger.ts @@ -23,7 +23,8 @@ export default ` -`/** +`; +/** * @copyright Copyright (c) 2021, Xgene Cloud Ltd * * @author Naveen MR diff --git a/packages/nocodb/src/lib/noco/rest/ui/emailTemplates/forgotPassword.ts b/packages/nocodb/src/lib/noco/rest/ui/emailTemplates/forgotPassword.ts index 615bf74e32..f59509132d 100644 --- a/packages/nocodb/src/lib/noco/rest/ui/emailTemplates/forgotPassword.ts +++ b/packages/nocodb/src/lib/noco/rest/ui/emailTemplates/forgotPassword.ts @@ -169,7 +169,7 @@ table[class=body] .article { -` +`; /** * @copyright Copyright (c) 2021, Xgene Cloud Ltd * diff --git a/packages/nocodb/src/lib/noco/rest/ui/emailTemplates/invite.ts b/packages/nocodb/src/lib/noco/rest/ui/emailTemplates/invite.ts index 0a1a545f35..8e143bd938 100644 --- a/packages/nocodb/src/lib/noco/rest/ui/emailTemplates/invite.ts +++ b/packages/nocodb/src/lib/noco/rest/ui/emailTemplates/invite.ts @@ -205,7 +205,7 @@ export default ` -` +`; /** * @copyright Copyright (c) 2021, Xgene Cloud Ltd diff --git a/packages/nocodb/src/lib/noco/rest/ui/emailTemplates/verify.ts b/packages/nocodb/src/lib/noco/rest/ui/emailTemplates/verify.ts index 5626709c98..673dd8f8b6 100644 --- a/packages/nocodb/src/lib/noco/rest/ui/emailTemplates/verify.ts +++ b/packages/nocodb/src/lib/noco/rest/ui/emailTemplates/verify.ts @@ -206,7 +206,7 @@ export default ` -` +`; /** * @copyright Copyright (c) 2021, Xgene Cloud Ltd diff --git a/packages/nocodb/src/lib/noco/upgrader/NcUpgrader.ts b/packages/nocodb/src/lib/noco/upgrader/NcUpgrader.ts index 594056258d..99626405a0 100644 --- a/packages/nocodb/src/lib/noco/upgrader/NcUpgrader.ts +++ b/packages/nocodb/src/lib/noco/upgrader/NcUpgrader.ts @@ -1,39 +1,38 @@ -import {NcConfig} from "../../../interface/config"; +import { NcConfig } from '../../../interface/config'; import debug from 'debug'; -import NcMetaIO from "../meta/NcMetaIO"; -import ncProjectEnvUpgrader from "./jobs/ncProjectEnvUpgrader"; +import NcMetaIO from '../meta/NcMetaIO'; +import ncProjectEnvUpgrader from './jobs/ncProjectEnvUpgrader'; const log = debug('nc:upgrader'); - export interface NcUpgraderCtx { - ncMeta: NcMetaIO + ncMeta: NcMetaIO; } export default class NcUpgrader { - private static STORE_KEY = 'NC_CONFIG_MAIN'; - // Todo: transaction public static async upgrade(ctx: NcUpgraderCtx): Promise { - this.log(`upgrade :`,) + this.log(`upgrade :`); try { await ctx.ncMeta.startTransaction(); const NC_VERSIONS: any[] = [ - {name: '0009000', handler: null}, - {name: '0009044', handler: null}, - {name: '0011043', handler: ncProjectEnvUpgrader} - ] - if (!await ctx.ncMeta.knexConnection?.schema?.hasTable?.('nc_store')) { + { name: '0009000', handler: null }, + { name: '0009044', handler: null }, + { name: '0011043', handler: ncProjectEnvUpgrader } + ]; + if (!(await ctx.ncMeta.knexConnection?.schema?.hasTable?.('nc_store'))) { return; } - this.log(`upgrade : Getting configuration from meta database`,) + this.log(`upgrade : Getting configuration from meta database`); - const config = await ctx.ncMeta.metaGet('', '', 'nc_store', {key: this.STORE_KEY}); + const config = await ctx.ncMeta.metaGet('', '', 'nc_store', { + key: this.STORE_KEY + }); if (config) { const configObj: NcConfig = JSON.parse(config.value); @@ -41,19 +40,28 @@ export default class NcUpgrader { for (const version of NC_VERSIONS) { // compare current version and old version if (version.name > configObj.version) { - this.log(`upgrade : Upgrading '%s' => '%s'`, configObj.version, version.name) + this.log( + `upgrade : Upgrading '%s' => '%s'`, + configObj.version, + version.name + ); await version?.handler?.(ctx); // update version in meta after each upgrade config.version = version.name; - await ctx.ncMeta.metaUpdate('', '', 'nc_store', { - value: JSON.stringify(config) - }, { - key: NcUpgrader.STORE_KEY, - }); + await ctx.ncMeta.metaUpdate( + '', + '', + 'nc_store', + { + value: JSON.stringify(config) + }, + { + key: NcUpgrader.STORE_KEY + } + ); // todo: backup data - } if (version.name === process.env.NC_VERSION) { break; @@ -66,7 +74,7 @@ export default class NcUpgrader { }); } } else { - this.log(`upgrade : Inserting config to meta database`,) + this.log(`upgrade : Inserting config to meta database`); const configObj: any = {}; const isOld = (await ctx.ncMeta.projectList())?.length; configObj.version = isOld ? '0009000' : process.env.NC_VERSION; @@ -78,19 +86,16 @@ export default class NcUpgrader { await this.upgrade(ctx); } } - await ctx.ncMeta.commit() + await ctx.ncMeta.commit(); } catch (e) { - await ctx.ncMeta.rollback(e) - console.log('Error', e) + await ctx.ncMeta.rollback(e); + console.log('Error', e); } } - private static log(str, ...args): void { log(`${str}`, ...args); } - - private - + private; } diff --git a/packages/nocodb/src/lib/noco/upgrader/jobs/ncProjectEnvUpgrader.ts b/packages/nocodb/src/lib/noco/upgrader/jobs/ncProjectEnvUpgrader.ts index 4088e23a1d..c369b97f35 100644 --- a/packages/nocodb/src/lib/noco/upgrader/jobs/ncProjectEnvUpgrader.ts +++ b/packages/nocodb/src/lib/noco/upgrader/jobs/ncProjectEnvUpgrader.ts @@ -1,20 +1,18 @@ -import {NcUpgraderCtx} from "../NcUpgrader"; +import { NcUpgraderCtx } from '../NcUpgrader'; -export default async function (ctx: NcUpgraderCtx) { - const projects = await ctx.ncMeta.projectList(); +export default async function(ctx: NcUpgraderCtx) { + const projects = await ctx.ncMeta.projectList(); - for (const project of projects) { + for (const project of projects) { + const projectConfig = JSON.parse(project.config); - const projectConfig = JSON.parse(project.config); + const envVal = projectConfig.envs?.dev; + projectConfig.workingEnv = '_noco'; - const envVal = projectConfig.envs?.dev; - projectConfig.workingEnv = '_noco' - - if (envVal) { - projectConfig.envs._noco = envVal; - delete projectConfig.envs.dev; - } - await ctx.ncMeta.projectUpdate(project?.id, projectConfig) + if (envVal) { + projectConfig.envs._noco = envVal; + delete projectConfig.envs.dev; } - + await ctx.ncMeta.projectUpdate(project?.id, projectConfig); + } } diff --git a/packages/nocodb/src/lib/sqlMgr/ProjectMgr.ts b/packages/nocodb/src/lib/sqlMgr/ProjectMgr.ts index 2547f7d598..9323a1eaa8 100644 --- a/packages/nocodb/src/lib/sqlMgr/ProjectMgr.ts +++ b/packages/nocodb/src/lib/sqlMgr/ProjectMgr.ts @@ -1,7 +1,6 @@ -import SqlMgr from "./SqlMgr"; +import SqlMgr from './SqlMgr'; export default class ProjectMgr { - public static make(): ProjectMgr { if (!ProjectMgr._instance) { ProjectMgr._instance = new ProjectMgr(); @@ -11,7 +10,7 @@ export default class ProjectMgr { private static _instance: ProjectMgr; private sqlMgrMap: { - [key: string]: SqlMgr + [key: string]: SqlMgr; }; constructor() { @@ -24,4 +23,4 @@ export default class ProjectMgr { } return this.sqlMgrMap[project.id]; } -} \ No newline at end of file +} diff --git a/packages/nocodb/src/lib/sqlMgr/SqlMgr.ts b/packages/nocodb/src/lib/sqlMgr/SqlMgr.ts index ac4125c42c..959f48af1c 100644 --- a/packages/nocodb/src/lib/sqlMgr/SqlMgr.ts +++ b/packages/nocodb/src/lib/sqlMgr/SqlMgr.ts @@ -1,19 +1,18 @@ -import fs from "fs"; -import path from "path"; -import url from 'url' +import fs from 'fs'; +import path from 'path'; +import url from 'url'; import fsExtra from 'fs-extra'; import importFresh from 'import-fresh'; import inflection from 'inflection'; -import {Debug, Result, SqlClientFactory} from "nc-help"; +import { Debug, Result, SqlClientFactory } from 'nc-help'; import slash from 'slash'; // import debug from 'debug'; -const log = new Debug("SqlMgr"); -import KnexMigrator from '../migrator/SqlMigrator/lib/KnexMigrator' +const log = new Debug('SqlMgr'); +import KnexMigrator from '../migrator/SqlMigrator/lib/KnexMigrator'; // import {XKnex} from "../dataMapper"; -import NcConnectionMgr from "../noco/common/NcConnectionMgr"; - +import NcConnectionMgr from '../noco/common/NcConnectionMgr'; const ToolOps = { DB_TABLE_LIST: 'tableList', @@ -63,7 +62,6 @@ const ToolOps = { DB_TABLE_INDEX_DELETE: 'indexDelete', DB_TABLE_ROW_DELETE: 'delete', - DB_GET_KNEX_DATA_TYPES: 'getKnexDataTypes', DB_PROJECT_OPEN_BY_WEB: 'DB_PROJECT_OPEN_BY_WEB', PROJECT_READ_BY_WEB: 'PROJECT_READ_BY_WEB', @@ -84,15 +82,12 @@ const ToolOps = { TEST_CONNECTION: 'testConnection', - PROJECT_CREATE_BY_WEB: 'projectCreateByWeb', PROJECT_CHANGE_ENV: 'projectChangeEnv', - PROJECT_UPDATE_BY_WEB: 'projectUpdateByWeb', - + PROJECT_UPDATE_BY_WEB: 'projectUpdateByWeb' }; - export default class SqlMgr { // @ts-ignore private project: any; @@ -123,14 +118,13 @@ export default class SqlMgr { * @memberof SqlMgr */ constructor(args: any = {}) { - const func = "constructor"; + const func = 'constructor'; log.api(`${func}:args:`, args); this.project = args; this.metaDb = args.metaDb; this.project_id = args.project_id = args.id; this._migrator = new KnexMigrator(args); - this.currentProjectJson = {}; this.currentProjectConnections = {}; this.currentProjectServers = {}; @@ -168,25 +162,31 @@ export default class SqlMgr { // todo: read it from config or env // this.currentProjectFolder = process.cwd(); - this.currentProjectFolder = this.currentProjectJson.toolDir || process.cwd(); + this.currentProjectFolder = + this.currentProjectJson.toolDir || process.cwd(); args.folder = slash(this.currentProjectFolder); - const projectJson = {...this.currentProjectJson, envs: {...this.currentProjectJson.envs}}; + const projectJson = { + ...this.currentProjectJson, + envs: { ...this.currentProjectJson.envs } + }; // delete db credentials for (const env of Object.keys(projectJson.envs)) { - projectJson.envs[env] = {...projectJson.envs[env], db: [...projectJson.envs[env].db]} + projectJson.envs[env] = { + ...projectJson.envs[env], + db: [...projectJson.envs[env].db] + }; for (let i = 0; i < projectJson.envs[env].db.length; i++) { projectJson.envs[env].db[i] = { ...projectJson.envs[env].db[i], connection: { database: projectJson.envs[env].db[i].connection.database } - } + }; } } - // remove meta db credentials if (projectJson.meta?.db) delete projectJson.meta.db; @@ -212,16 +212,23 @@ export default class SqlMgr { // log.ppe(e, _func); // throw e; // } - result.data.list = [{ - folder: args.folder - }]; + result.data.list = [ + { + folder: args.folder + } + ]; log.api(`${_func}: result`, result); return result; - } public projectGetFolder(args) { - return path.join(this.currentProjectFolder, 'server', 'tool', args.dbAlias, 'seeds'); + return path.join( + this.currentProjectFolder, + 'server', + 'tool', + args.dbAlias, + 'seeds' + ); } public getRouteVersionLetter(args) { @@ -232,23 +239,31 @@ export default class SqlMgr { if (db.meta && db.meta.api && db.meta.api.prefix) { return db.meta.api.prefix; } - return this.genVer(index) + return this.genVer(index); } } } public genVer(i) { const l = 'vwxyzabcdefghijklmnopqrstu'; - return i - .toString(26) - .split('') - .map(v => l[parseInt(v, 26)]) - .join('') + '1'; + return ( + i + .toString(26) + .split('') + .map(v => l[parseInt(v, 26)]) + .join('') + '1' + ); } - public projectGetGqlPolicyPath(args) { - return path.join(this.currentProjectFolder, 'server', 'resolvers', args.dbAlias, args.tn, `${args.tn}.policy.js`); + return path.join( + this.currentProjectFolder, + 'server', + 'resolvers', + args.dbAlias, + args.tn, + `${args.tn}.policy.js` + ); } public async projectOpenByWeb(args) { @@ -260,11 +275,9 @@ export default class SqlMgr { const data = new Result(); data.data.list = []; - // todo: read it from config or env this.currentProjectFolder = args.toolDir || process.cwd(); - this.currentProjectJson = args; args.folder = slash(this.currentProjectFolder); @@ -276,43 +289,40 @@ export default class SqlMgr { JSON.stringify(this.currentProjectJson.envs[env].db[i]) ); - const connectionKey = `${env}_${ - this.currentProjectJson.envs[env].db[i].meta.dbAlias - }`; - - this.currentProjectConnections[connectionKey] = SqlClientFactory.create( - {...connectionConfig, knex: NcConnectionMgr.get({ - dbAlias:this.currentProjectJson.envs[env].db[i].meta.dbAlias, - env:env, - config:args, - projectId:args.id - })} - ); + const connectionKey = `${env}_${this.currentProjectJson.envs[env].db[i].meta.dbAlias}`; + + this.currentProjectConnections[ + connectionKey + ] = SqlClientFactory.create({ + ...connectionConfig, + knex: NcConnectionMgr.get({ + dbAlias: this.currentProjectJson.envs[env].db[i].meta.dbAlias, + env: env, + config: args, + projectId: args.id + }) + }); this.currentProjectServers[connectionKey] = { xserver: null, input: {}, output: {} }; - } } // args.projectJson = JSON.parse(JSON.stringify(this.currentProjectJson)); data.data.list[0] = args; - this.projectOpenData = args; return data; - } catch (e) { console.log('projectOpen::error', e); throw e; } } - /** * * @@ -332,7 +342,6 @@ export default class SqlMgr { return this.currentProjectConnections[connectionKey]; } - let connectionConfig = {}; const dbs = this.currentProjectJson.envs[args.env].db; @@ -343,7 +352,7 @@ export default class SqlMgr { } } - if ("client" in connectionConfig) { + if ('client' in connectionConfig) { const data = SqlClientFactory.create(connectionConfig); this.currentProjectConnections[connectionKey] = data; // console.log(data); @@ -353,9 +362,7 @@ export default class SqlMgr { throw new Error(`Could not find connectionconfig ${args}`); } - public async projectGetSchemaKey(args) { - const func = this.projectGetSqlClient.name; log.api(`${func}:args:`, args); @@ -378,18 +385,19 @@ export default class SqlMgr { if (connectionConfig.client === 'sqlite3') { schemaKey = connectionConfig.connection.connection.filename; } else { - schemaKey = connectionConfig.connection.host + '_' + - connectionConfig.connection.port + '_' + + schemaKey = + connectionConfig.connection.host + + '_' + + connectionConfig.connection.port + + '_' + connectionConfig.connection.database; } return schemaKey; throw new Error(`Could not find connectionconfig ${args}`); - } - public async projectCreateByWeb(args) { const func = this.projectCreateByWeb.name; const result = new Result(); @@ -398,7 +406,6 @@ export default class SqlMgr { console.log(args); try { - args.folder = args.folder || args.project.folder; args.folder = path.dirname(args.folder); args.title = args.title || args.project.title; @@ -412,7 +419,6 @@ export default class SqlMgr { } this.projectOpenByWeb(args.projectJson); - } catch (error) { log.ppe(error, func); } @@ -420,19 +426,16 @@ export default class SqlMgr { return result; } - public async projectUpdateByWeb(args) { const func = this.projectUpdateByWeb.name; - const result = new Result(); log.api(`${func}:args:`, args); console.log(args); try { - - fs.unlinkSync(path.join(this.currentProjectFolder, 'config.xc.json')) + fs.unlinkSync(path.join(this.currentProjectFolder, 'config.xc.json')); args.folder = args.folder || args.project.folder; args.folder = path.dirname(args.folder); @@ -440,14 +443,12 @@ export default class SqlMgr { args.type = args.type || args.project.type; if (this.isDbConnectionProject(args.projectJson)) { - } else { await this.migrator().init(args); await this.migrator().sync(args); } this.projectOpenByWeb(args.projectJson); - } catch (error) { log.ppe(error, func); } @@ -455,7 +456,6 @@ export default class SqlMgr { return result; } - public async projectUpdateWeb(args) { const func = this.projectUpdateWeb.name; const result = new Result(); @@ -468,14 +468,12 @@ export default class SqlMgr { args.type = args.type || args.project.type; if (this.isDbConnectionProject(args.projectJson)) { - } else { await this.migrator().init(args); await this.migrator().sync(args); } this.projectOpenByWeb(args.project); - } catch (error) { log.ppe(error, func); } @@ -507,15 +505,14 @@ export default class SqlMgr { return 'pg'; break; default: - return 'mysql' + return 'mysql'; } } public _getKnexInitObject(sqlConfig) { - // console.log(sqlConfig); - const ORACLE_PORT = 1521 + const ORACLE_PORT = 1521; if (sqlConfig.typeOfDatabase === 'sqlite3') { return { @@ -524,7 +521,7 @@ export default class SqlMgr { // filename: "./db/sakila-sqlite" filename: sqlConfig.database } - } + }; } else if (sqlConfig.typeOfDatabase === 'oracledb') { return { client: sqlConfig.typeOfDatabase, @@ -534,27 +531,27 @@ export default class SqlMgr { password: sqlConfig.password, database: sqlConfig.database, port: sqlConfig.port, - connectString: `localhost:${ORACLE_PORT}/xe`, + connectString: `localhost:${ORACLE_PORT}/xe` // connectString: `${sqlConfig.host}:${sqlConfig.port}/${sqlConfig.database}`, } - } + }; } else if (sqlConfig.typeOfDatabase === 'mariadb') { sqlConfig.typeOfDatabase = 'mysql2'; return { client: sqlConfig.typeOfDatabase, connection: sqlConfig - } + }; } else if (sqlConfig.typeOfDatabase === 'cockroachdb') { sqlConfig.typeOfDatabase = 'pg'; return { client: sqlConfig.typeOfDatabase, connection: sqlConfig - } + }; } else { return { client: sqlConfig.typeOfDatabase, - connection: {...sqlConfig} - } + connection: { ...sqlConfig } + }; } } @@ -577,21 +574,19 @@ export default class SqlMgr { return 0; break; default: - return 'mysql' + return 'mysql'; } } public _parseUrlToConnection(dbUrl) { - try { - const config: any = {}; config.connection = {}; config.meta = { tn: 'nc_evolutions', dbAlias: 'db' - } + }; const urlParts = url.parse(dbUrl, true); @@ -599,7 +594,8 @@ export default class SqlMgr { config.client = this._getDatabaseType(urlParts.protocol) || 'mysql'; config.connection.host = urlParts.hostname || 'localhost'; - config.connection.port = +urlParts.port || this._getDbPort(config.typeOfDatabase); + config.connection.port = + +urlParts.port || this._getDbPort(config.typeOfDatabase); config.connection.user = queryParams.u || null; config.connection.password = queryParams.p || null; config.connection.database = queryParams.d || null; @@ -615,7 +611,6 @@ export default class SqlMgr { } return config; - } catch (e) { console.log(e); throw e; @@ -623,20 +618,18 @@ export default class SqlMgr { } public _createProjectJsonFromDbUrls(args) { - try { - const projectJson = { title: '', envs: { - "_noco": { + _noco: { db: [], apiClient: { data: [] } } }, - "workingEnv": "_noco", + workingEnv: '_noco', meta: { version: '0.5', seedsFolder: 'seeds', @@ -656,23 +649,19 @@ export default class SqlMgr { apiClient: { data: [] } - } - + }; for (let i = 0; i < args.url.length; ++i) { - - const config = this._parseUrlToConnection(args.url[i]) + const config = this._parseUrlToConnection(args.url[i]); if (i) { config.meta.dbAlias = i > 1 ? `secondary${i}` : `secondary`; } projectJson.envs._noco.db.push(config); - } return projectJson; - } catch (e) { console.log(e); throw e; @@ -712,13 +701,11 @@ export default class SqlMgr { args.type = args.type || args.project.type; if (this.isDbConnectionProject(args.projectJson)) { - } else { await this.migrator().init(args); await this.migrator().sync(args); } // this.projectOpen(args.project); - } catch (error) { log.ppe(error, func); } @@ -726,7 +713,6 @@ export default class SqlMgr { return result; } - /** * * @@ -756,7 +742,6 @@ export default class SqlMgr { * @memberof SqlMgr */ public async sqlOp(args, op, opArgs) { - const func = this.sqlOp.name; log.api(`${func}:args:`, args, op, opArgs); @@ -768,11 +753,9 @@ export default class SqlMgr { // do sql operation const data = await client[op](opArgs); - return data - + return data; } - /** * * @@ -784,7 +767,6 @@ export default class SqlMgr { * @memberof SqlMgr */ public async sqlOpPlus(args, op, opArgs) { - const func = this.sqlOpPlus.name; log.api(`${func}:args:`, args, op, opArgs); @@ -803,7 +785,6 @@ export default class SqlMgr { args.folder = this.currentProjectFolder; if (this.isProjectDbConnection()) { - } else { // create sql migration files const sqlMigrationFiles = await this.migrator().migrationsCreate(args); @@ -838,18 +819,16 @@ export default class SqlMgr { await this.migrator().migrationsUp(migrationArgs); } - return sqlMigrationStatements; } public createExpressRoutes(tables, relations, router = 'express') { const routes = []; - const id = router === 'express' ? ':id' : '{id}' - const parentId = router === 'express' ? ':parentId' : '{parentId}' + const id = router === 'express' ? ':id' : '{id}'; + const parentId = router === 'express' ? ':parentId' : '{parentId}'; for (let i = 0; i < tables.length; ++i) { - /**************** START : express routes ****************/ routes.push({ type: 'get', @@ -891,7 +870,6 @@ export default class SqlMgr { enabled: true }); - const hasManyRelations = relations.filter(r => r.tn === tables[i].tn); for (let j = 0; j < hasManyRelations.length; ++j) { @@ -911,42 +889,59 @@ export default class SqlMgr { return routes; } - - public getDbType({env, dbAlias}) { - const db = this.currentProjectJson.envs[env].db.find(db => db.meta.dbAlias === dbAlias) + public getDbType({ env, dbAlias }) { + const db = this.currentProjectJson.envs[env].db.find( + db => db.meta.dbAlias === dbAlias + ); return db.client; } public async copyAuthMigrations(args) { - try { - const dbs = this.currentProjectJson.envs._noco.db; const dbType = dbs[0].client; console.time('Copy and delete auth user migrations'); - const sqlClient = await this.projectGetSqlClient({env: '_noco', dbAlias: 'db'}); - const usersTableExists = await sqlClient.hasTable({tn: 'xc_users'}); + const sqlClient = await this.projectGetSqlClient({ + env: '_noco', + dbAlias: 'db' + }); + const usersTableExists = await sqlClient.hasTable({ tn: 'xc_users' }); if (usersTableExists && usersTableExists.data.value) { console.log('A users table already exists, skip auth migrations'); return; } - if (!args.noauth) { - await fsExtra.copy(path.join(this.currentProjectFolder, 'server', 'tool', 'misc', 'auth', dbType), path.join(this.currentProjectFolder, 'server', 'tool', 'db', 'migrations')) - await fsExtra.remove(path.join(this.currentProjectFolder, 'server', 'tool', 'misc')) + await fsExtra.copy( + path.join( + this.currentProjectFolder, + 'server', + 'tool', + 'misc', + 'auth', + dbType + ), + path.join( + this.currentProjectFolder, + 'server', + 'tool', + 'db', + 'migrations' + ) + ); + await fsExtra.remove( + path.join(this.currentProjectFolder, 'server', 'tool', 'misc') + ); } console.timeEnd('Copy and delete auth user migrations'); } catch (e) { - console.log('auth migration copying', e) + console.log('auth migration copying', e); } - } - public isProjectRest() { return this.currentProjectJson.projectType.toLowerCase() === 'rest'; } @@ -968,8 +963,10 @@ export default class SqlMgr { } public isProjectNoApis() { - return this.currentProjectJson.projectType.toLowerCase() === 'dbconnection' - || this.currentProjectJson.projectType.toLowerCase() === 'migrations'; + return ( + this.currentProjectJson.projectType.toLowerCase() === 'dbconnection' || + this.currentProjectJson.projectType.toLowerCase() === 'migrations' + ); } public isRestProject(projectJson) { @@ -977,7 +974,10 @@ export default class SqlMgr { } public isMvc() { - return this.currentProjectJson.type && this.currentProjectJson.type.toLowerCase() === 'mvc'; + return ( + this.currentProjectJson.type && + this.currentProjectJson.type.toLowerCase() === 'mvc' + ); } public isGraphqlProject(projectJson) { @@ -993,11 +993,12 @@ export default class SqlMgr { } public isNoApisProject(projectJson) { - return projectJson.projectType.toLowerCase() === 'dbConnection' - || projectJson.projectType.toLowerCase() === 'migrations'; + return ( + projectJson.projectType.toLowerCase() === 'dbConnection' || + projectJson.projectType.toLowerCase() === 'migrations' + ); } - public async handleApiCall(apiMeta) { const req = this.axiosRequestMake(apiMeta); // t = process.hrtime(); @@ -1037,21 +1038,25 @@ export default class SqlMgr { apiMeta.response = {}; const req = { - params: apiMeta.parameters ? apiMeta.parameters.reduce((paramsObj, param) => { - if (param.name && param.enabled) { - paramsObj[param.name] = param.value; - } - return paramsObj; - }, {}) : {}, + params: apiMeta.parameters + ? apiMeta.parameters.reduce((paramsObj, param) => { + if (param.name && param.enabled) { + paramsObj[param.name] = param.value; + } + return paramsObj; + }, {}) + : {}, url: apiMeta.path, method: apiMeta.method, data: apiMeta.body, - headers: apiMeta.headers ? apiMeta.headers.reduce((headersObj, header) => { - if (header.name && header.enabled) { - headersObj[header.name] = header.value; - } - return headersObj; - }, {}) : {}, + headers: apiMeta.headers + ? apiMeta.headers.reduce((headersObj, header) => { + if (header.name && header.enabled) { + headersObj[header.name] = header.value; + } + return headersObj; + }, {}) + : {}, withCredentials: true }; return req; @@ -1078,268 +1083,273 @@ export default class SqlMgr { return client.raw(query); } - public async projectChangeEnv(args) { try { - const xcConfig = JSON.parse(fs.readFileSync(path.join(this.currentProjectFolder, 'config.xc.json'), 'utf8')); + const xcConfig = JSON.parse( + fs.readFileSync( + path.join(this.currentProjectFolder, 'config.xc.json'), + 'utf8' + ) + ); xcConfig.workingEnv = args.env; - fs.writeFileSync(path.join(this.currentProjectFolder, 'config.xc.json'), JSON.stringify(xcConfig, null, 2)); + fs.writeFileSync( + path.join(this.currentProjectFolder, 'config.xc.json'), + JSON.stringify(xcConfig, null, 2) + ); } catch (e) { console.log(e); throw e; } } - // table alias functions - public async getTableNameAlias({inflectionFn, tn}) { + public async getTableNameAlias({ inflectionFn, tn }) { if (inflectionFn) { return inflection[inflectionFn](tn); } return tn; } - public async getColumnNameAlias({inflectionFn, cn}) { + public async getColumnNameAlias({ inflectionFn, cn }) { if (inflectionFn) { return inflection[inflectionFn](cn); } return cn; } - public async handleRequest(operation, args) { let result; try { - - - const op = (args.sqlOpPlus && !process.env.NC_TRY && !('NC_MIGRATIONS_DISABLED' in process.env) ? this.sqlOpPlus : this.sqlOp).bind(this); + const op = (args.sqlOpPlus && + !process.env.NC_TRY && + !('NC_MIGRATIONS_DISABLED' in process.env) + ? this.sqlOpPlus + : this.sqlOp + ).bind(this); switch (operation) { - case 'tableCreateStatement': + case 'tableCreateStatement': console.log('Within tableCreateStatement handler', args); result = await op(args, 'tableCreateStatement', args.args); break; - case 'tableInsertStatement': + case 'tableInsertStatement': console.log('Within tableInsertStatement handler', args); result = await op(args, 'tableInsertStatement', args.args); break; - case 'tableUpdateStatement': + case 'tableUpdateStatement': console.log('Within tableUpdateStatement handler', args); result = await op(args, 'tableUpdateStatement', args.args); break; - case 'tableSelectStatement': + case 'tableSelectStatement': console.log('Within tableSelectStatement handler', args); result = await op(args, 'tableSelectStatement', args.args); break; - case 'tableDeleteStatement': + case 'tableDeleteStatement': console.log('Within tableDeleteStatement handler', args); result = await op(args, 'tableDeleteStatement', args.args); break; - case ToolOps.DB_TABLE_LIST: + case ToolOps.DB_TABLE_LIST: console.log('Within DB_TABLE_LIST handler', args); result = await op(args, 'tableList', args.args); break; - case ToolOps.DB_VIEW_LIST: + case ToolOps.DB_VIEW_LIST: console.log('Within DB_VIEW_LIST handler', args); result = await op(args, 'viewList', args.args); break; - case ToolOps.DB_FUNCTION_LIST: + case ToolOps.DB_FUNCTION_LIST: console.log('Within DB_FUNCTION_LIST handler', args); result = await op(args, 'functionList', args.args); break; - case ToolOps.DB_SEQUENCE_LIST: + case ToolOps.DB_SEQUENCE_LIST: console.log('Within DB_SEQUENCE_LIST handler', args); result = await op(args, 'sequenceList', args.args); break; - case ToolOps.DB_PROCEDURE_LIST: + case ToolOps.DB_PROCEDURE_LIST: console.log('Within DB_PROCEDURE_LIST handler', args); result = await op(args, 'procedureList', args.args); break; - case ToolOps.DB_TABLE_COLUMN_LIST: + case ToolOps.DB_TABLE_COLUMN_LIST: console.log('Within DB_TABLE_COLUMN_LIST handler', args); result = await op(args, 'columnList', args.args); break; - case ToolOps.DB_TABLE_TRIGGER_LIST: + case ToolOps.DB_TABLE_TRIGGER_LIST: console.log('Within DB_TABLE_TRIGGER_LIST handler', args); result = await op(args, 'triggerList', args.args); break; - case ToolOps.DB_TABLE_RELATION_LIST: + case ToolOps.DB_TABLE_RELATION_LIST: console.log('Within DB_TABLE_RELATION_LIST handler', args); result = await op(args, 'relationList', args.args); break; - case ToolOps.DB_TABLE_RELATION_LIST_ALL: + case ToolOps.DB_TABLE_RELATION_LIST_ALL: console.log('Within DB_TABLE_RELATION_LIST_ALL handler', args); result = await op(args, 'relationListAll', args.args); break; - case ToolOps.DB_TABLE_INDEX_LIST: + case ToolOps.DB_TABLE_INDEX_LIST: console.log('Within DB_TABLE_INDEX_LIST handler', args); result = await op(args, 'indexList', args.args); break; - case ToolOps.DB_TABLE_ROW_LIST: + case ToolOps.DB_TABLE_ROW_LIST: console.log('Within DB_TABLE_ROW_LIST handler', args); result = await op(args, 'list', args.args); break; - case ToolOps.DB_TABLE_RENAME: + case ToolOps.DB_TABLE_RENAME: console.log('Within DB_TABLE_RENAME handler', args); result = await op(args, 'tableRename', args.args); break; - case ToolOps.DB_TABLE_CREATE: + case ToolOps.DB_TABLE_CREATE: console.log('Within DB_TABLE_CREATE handler', args); result = await op(args, 'tableCreate', args.args); break; - case ToolOps.DB_VIEW_CREATE: + case ToolOps.DB_VIEW_CREATE: console.log('Within DB_VIEW_CREATE handler', args); result = await op(args, 'viewCreate', args.args); break; - case ToolOps.DB_FUNCTION_CREATE: + case ToolOps.DB_FUNCTION_CREATE: console.log('Within DB_FUNCTION_CREATE handler', args); result = await op(args, 'functionCreate', args.args); break; - case ToolOps.DB_SEQUENCE_CREATE: + case ToolOps.DB_SEQUENCE_CREATE: console.log('Within DB_SEQUENCE_CREATE handler', args); result = await op(args, 'sequenceCreate', args.args); break; - case ToolOps.DB_PROCEDURE_CREATE: + case ToolOps.DB_PROCEDURE_CREATE: console.log('Within DB_PROCEDURE_CREATE handler', args); result = await op(args, 'procedureCreate', args.args); break; - case ToolOps.DB_TABLE_TRIGGER_CREATE: + case ToolOps.DB_TABLE_TRIGGER_CREATE: console.log('Within DB_TABLE_TRIGGER_CREATE handler', args); result = await op(args, 'triggerCreate', args.args); break; - case ToolOps.DB_TABLE_RELATION_CREATE: + case ToolOps.DB_TABLE_RELATION_CREATE: console.log('Within DB_TABLE_RELATION_CREATE handler', args); result = await op(args, 'relationCreate', args.args); break; - case ToolOps.DB_TABLE_INDEX_CREATE: + case ToolOps.DB_TABLE_INDEX_CREATE: console.log('Within DB_TABLE_INDEX_CREATE handler', args); result = await op(args, 'indexCreate', args.args); break; - case ToolOps.DB_TABLE_ROW_CREATE: + case ToolOps.DB_TABLE_ROW_CREATE: console.log('Within DB_TABLE_ROW_CREATE handler', args); result = await op(args, 'insert', args.args); break; - case ToolOps.DB_TABLE_UPDATE: + case ToolOps.DB_TABLE_UPDATE: console.log('Within DB_TABLE_UPDATE handler', args); result = await op(args, 'tableUpdate', args.args); break; - case ToolOps.DB_VIEW_UPDATE: + case ToolOps.DB_VIEW_UPDATE: console.log('Within DB_VIEW_UPDATE handler', args); result = await op(args, 'viewUpdate', args.args); break; - case ToolOps.DB_FUNCTION_UPDATE: + case ToolOps.DB_FUNCTION_UPDATE: console.log('Within DB_FUNCTION_UPDATE handler', args); result = await op(args, 'functionUpdate', args.args); break; - case ToolOps.DB_SEQUENCE_UPDATE: + case ToolOps.DB_SEQUENCE_UPDATE: console.log('Within DB_SEQUENCE_UPDATE handler', args); result = await op(args, 'sequenceUpdate', args.args); break; - case ToolOps.DB_PROCEDURE_UPDATE: + case ToolOps.DB_PROCEDURE_UPDATE: console.log('Within DB_PROCEDURE_UPDATE handler', args); result = await op(args, 'procedureUpdate', args.args); break; - case ToolOps.DB_TABLE_TRIGGER_UPDATE: + case ToolOps.DB_TABLE_TRIGGER_UPDATE: console.log('Within DB_TABLE_TRIGGER_UPDATE handler', args); result = await op(args, 'triggerUpdate', args.args); break; - case ToolOps.DB_TABLE_RELATION_UPDATE: + case ToolOps.DB_TABLE_RELATION_UPDATE: console.log('Within DB_TABLE_RELATION_UPDATE handler', args); result = await op(args, 'relationUpdate', args.args); break; - case ToolOps.DB_TABLE_INDEX_UPDATE: + case ToolOps.DB_TABLE_INDEX_UPDATE: console.log('Within DB_TABLE_INDEX_UPDATE handler', args); result = await op(args, 'indexUpdate', args.args); break; - case ToolOps.DB_TABLE_ROW_UPDATE: + case ToolOps.DB_TABLE_ROW_UPDATE: console.log('Within DB_TABLE_ROW_UPDATE handler', args); result = await op(args, 'update', args.args); break; - case ToolOps.DB_TABLE_DELETE: + case ToolOps.DB_TABLE_DELETE: console.log('Within DB_TABLE_DELETE handler', args); result = await op(args, 'tableDelete', args.args); break; - case ToolOps.DB_VIEW_DELETE: + case ToolOps.DB_VIEW_DELETE: console.log('Within DB_VIEW_DELETE handler', args); result = await op(args, 'viewDelete', args.args); break; - case ToolOps.DB_FUNCTION_DELETE: + case ToolOps.DB_FUNCTION_DELETE: console.log('Within DB_FUNCTION_DELETE handler', args); result = await op(args, 'functionDelete', args.args); break; - case ToolOps.DB_SEQUENCE_DELETE: + case ToolOps.DB_SEQUENCE_DELETE: console.log('Within DB_SEQUENCE_DELETE handler', args); result = await op(args, 'sequenceDelete', args.args); break; - case ToolOps.DB_PROCEDURE_DELETE: + case ToolOps.DB_PROCEDURE_DELETE: console.log('Within DB_PROCEDURE_DELETE handler', args); result = await op(args, 'procedureDelete', args.args); break; - case ToolOps.DB_TABLE_TRIGGER_DELETE: + case ToolOps.DB_TABLE_TRIGGER_DELETE: console.log('Within DB_TABLE_TRIGGER_DELETE handler', args); result = await op(args, 'triggerDelete', args.args); break; - case ToolOps.DB_TABLE_RELATION_DELETE: + case ToolOps.DB_TABLE_RELATION_DELETE: console.log('Within DB_TABLE_RELATION_DELETE handler', args); result = await op(args, 'relationDelete', args.args); break; - case ToolOps.DB_TABLE_INDEX_DELETE: + case ToolOps.DB_TABLE_INDEX_DELETE: console.log('Within DB_TABLE_INDEX_DELETE handler', args); result = await op(args, 'indexDelete', args.args); break; - case ToolOps.DB_TABLE_ROW_DELETE: + case ToolOps.DB_TABLE_ROW_DELETE: console.log('Within DB_TABLE_ROW_DELETE handler', args); result = await op(args, 'delete', args.args); break; - case ToolOps.DB_GET_KNEX_DATA_TYPES: + case ToolOps.DB_GET_KNEX_DATA_TYPES: console.log('Within DB_TABLE_ROW_DELETE handler', args); result = await op(args, 'getKnexDataTypes', args.args); break; - case ToolOps.DB_PROJECT_OPEN_BY_WEB: + case ToolOps.DB_PROJECT_OPEN_BY_WEB: console.log('Within DB_PROJECT_OPEN handler', args); result = ''; break; - case ToolOps.PROJECT_READ_BY_WEB: + case ToolOps.PROJECT_READ_BY_WEB: console.log('Within PROJECT_READ_BY_WEB handler', args); result = this.projectReadByWeb({}); break; - case ToolOps.DB_VIEW_READ: + case ToolOps.DB_VIEW_READ: console.log('Within DB_VIEW_READ handler', args); result = await op(args, 'viewRead', args.args); break; - case ToolOps.DB_FUNCTION_READ: + case ToolOps.DB_FUNCTION_READ: console.log('Within DB_FUNCTION_READ handler', args); result = await op(args, 'functionRead', args.args); break; - case ToolOps.DB_PROCEDURE_READ: + case ToolOps.DB_PROCEDURE_READ: console.log('Within DB_FUNCTION_READ handler', args); result = await op(args, 'procedureRead', args.args); break; - - case ToolOps.IMPORT_FRESH: + case ToolOps.IMPORT_FRESH: console.log('Within IMPORT_FRESH handler', args); result = await importFresh(args.args.path); break; - case ToolOps.WRITE_FILE: + case ToolOps.WRITE_FILE: console.log('Within WRITE_FILE handler', args); result = fs.writeFileSync(args.args.path, args.args.data); break; - - case ToolOps.REST_API_CALL: + case ToolOps.REST_API_CALL: console.log('Within REST_API_CALL handler', args); result = this.handleApiCall(args.args); break; - case ToolOps.PROJECT_MIGRATIONS_LIST: console.log('Within PROJECT_MIGRATIONS_LIST handler', args); result = await this.migrationsList(args.args); @@ -1362,13 +1372,11 @@ export default class SqlMgr { result = await this.executeRawQuery(args, args.args); break; - case ToolOps.PROJECT_HAS_DB: console.log('Within PROJECT_HAS_DB handler', args); result = await this.projectHasDb(); break; - case ToolOps.TEST_CONNECTION: console.log('Within TEST_CONNECTION handler', args); result = await this.testConnection(args.args); @@ -1379,7 +1387,6 @@ export default class SqlMgr { result = await this.projectCreateByWeb(args.args); break; - case ToolOps.PROJECT_UPDATE_BY_WEB: console.log('Within PROJECT_UPDATE_BY_WEB handler', args); result = await this.projectUpdateByWeb(args.args); @@ -1390,7 +1397,6 @@ export default class SqlMgr { result = await this.projectChangeEnv(args.args); break; - case 'tableMetaCreate': case 'tableMetaDelete': case 'tableMetaRecreate': @@ -1403,11 +1409,11 @@ export default class SqlMgr { case 'functionMetaCreate': case 'functionMetaDelete': case 'functionMetaRecreate': - result = {msg: 'success'}; + result = { msg: 'success' }; break; default: - throw new Error('Operation not found') + throw new Error('Operation not found'); break; } } catch (e) { @@ -1420,18 +1426,14 @@ export default class SqlMgr { public async handleRequestWithFile(operation, _args, _file) { let result; - try { // console.log(operation, args); - // const op = (args.sqlOpPlus ? this.sqlOpPlus : this.sqlOp).bind(this); switch (operation) { - - default: - throw new Error('Operation not found') + throw new Error('Operation not found'); break; } } catch (e) { @@ -1441,9 +1443,8 @@ export default class SqlMgr { return result; } - public projectHasDb() { - for (const env of (Object.values(this.currentProjectJson.envs) as any[])) { + for (const env of Object.values(this.currentProjectJson.envs) as any[]) { if (env.db.length) { return true; } @@ -1452,11 +1453,6 @@ export default class SqlMgr { } public static stats: any; - } SqlMgr.stats = SqlMgr.stats || {}; - - - - diff --git a/packages/nocodb/src/lib/sqlMgr/code/BaseRender.ts b/packages/nocodb/src/lib/sqlMgr/code/BaseRender.ts index a3edaa2eef..d0f43d7e54 100644 --- a/packages/nocodb/src/lib/sqlMgr/code/BaseRender.ts +++ b/packages/nocodb/src/lib/sqlMgr/code/BaseRender.ts @@ -8,8 +8,8 @@ // import fsExtra from "fs-extra"; // import md5 from "md5"; // import dayjs from "dayjs";import Emit from "../../migrator/util/emit"; -import Debug from "../../migrator/util/Debug"; -import Emit from "../../migrator/util/emit"; +import Debug from '../../migrator/util/Debug'; +import Emit from '../../migrator/util/emit'; // const beautify = js_beautify.js; @@ -21,7 +21,6 @@ class BaseRender { protected log: any; protected ejsContent: any; - /** * Class responsible for rendering code * @@ -29,17 +28,17 @@ class BaseRender { * @param {string} - filename - filename of file to be rendered * @param {Object} - ctx - context to render this file */ - constructor({dir, filename, ctx}) { + constructor({ dir, filename, ctx }) { this.dir = dir; this.filename = filename; this.ctx = ctx; this.evt = new Emit(); - this.log = new Debug("BaseRender"); + this.log = new Debug('BaseRender'); } emit(data) { this.log.api(data); - this.evt.evt.emit("UI", { + this.evt.evt.emit('UI', { status: 0, data: `File : ${data}` }); @@ -47,7 +46,7 @@ class BaseRender { emitW(data) { this.log.warn(data); - this.evt.evt.emit("UI", { + this.evt.evt.emit('UI', { status: 1, data: `File : ${data}` }); @@ -55,7 +54,7 @@ class BaseRender { emitE(data) { this.log.error(data); - this.evt.evt.emit("UI", { + this.evt.evt.emit('UI', { status: -1, data: `File : ${data}` }); @@ -78,7 +77,7 @@ class BaseRender { * @param {Boolean} - force - on true overwrites the file * @returns {Promise} */ -/* + /* async render(obj) { const {ejsPath, ejsData, force = false, writeFile = true} = obj; const {ejsContent} = this; @@ -129,8 +128,6 @@ class BaseRender { } */ - - } export default BaseRender; diff --git a/packages/nocodb/src/lib/sqlMgr/code/gql-policies/xc-ts/ExpressXcTsPolicyGql.ts b/packages/nocodb/src/lib/sqlMgr/code/gql-policies/xc-ts/ExpressXcTsPolicyGql.ts index 42c8ffab99..c1ac6a21d0 100644 --- a/packages/nocodb/src/lib/sqlMgr/code/gql-policies/xc-ts/ExpressXcTsPolicyGql.ts +++ b/packages/nocodb/src/lib/sqlMgr/code/gql-policies/xc-ts/ExpressXcTsPolicyGql.ts @@ -1,8 +1,6 @@ -import BaseRender from "../../BaseRender"; +import BaseRender from '../../BaseRender'; class ExpressXcPolicyGql extends BaseRender { - - /** * * @param dir @@ -12,142 +10,219 @@ class ExpressXcPolicyGql extends BaseRender { * @param ctx.columns * @param ctx.relations */ - constructor({dir, filename, ctx}) { - super({dir, filename, ctx}); + constructor({ dir, filename, ctx }) { + super({ dir, filename, ctx }); } /** * Prepare variables used in code template */ - prepare():any { - + prepare(): any { let data = {}; /* example of simple variable */ data = this.ctx; return data; - } - getObject() { return { - [`${this.ctx.tn_camelize}List`]: {admin: true, user: true, guest: true}, - [`${this.ctx.tn_camelize}Read`]: {admin: true, user: true, guest: true}, - [`${this.ctx.tn_camelize}Create`]: {admin: true, user: true, guest: false}, - [`${this.ctx.tn_camelize}Update`]: {admin: true, user: true, guest: false}, - [`${this.ctx.tn_camelize}Delete`]: {admin: true, user: true, guest: false}, - [`${this.ctx.tn_camelize}Exists`]: {admin: true, user: true, guest: true}, - [`${this.ctx.tn_camelize}FindOne`]: {admin: true, user: true, guest: true}, - [`${this.ctx.tn_camelize}Count`]: {admin: true, user: true, guest: true}, - [`${this.ctx.tn_camelize}Distinct`]: {admin: true, user: true, guest: true}, - [`${this.ctx.tn_camelize}GroupBy`]: {admin: true, user: true, guest: true}, - [`${this.ctx.tn_camelize}Aggregate`]: {admin: true, user: true, guest: true}, - [`${this.ctx.tn_camelize}Distribution`]: {admin: true, user: true, guest: true}, - [`${this.ctx.tn_camelize}CreateBulk`]: {admin: true, user: true, guest: false}, - [`${this.ctx.tn_camelize}UpdateBulk`]: {admin: true, user: true, guest: false}, - [`${this.ctx.tn_camelize}DeleteBulk`]: {admin: true, user: true, guest: false} - } + [`${this.ctx.tn_camelize}List`]: { admin: true, user: true, guest: true }, + [`${this.ctx.tn_camelize}Read`]: { admin: true, user: true, guest: true }, + [`${this.ctx.tn_camelize}Create`]: { + admin: true, + user: true, + guest: false + }, + [`${this.ctx.tn_camelize}Update`]: { + admin: true, + user: true, + guest: false + }, + [`${this.ctx.tn_camelize}Delete`]: { + admin: true, + user: true, + guest: false + }, + [`${this.ctx.tn_camelize}Exists`]: { + admin: true, + user: true, + guest: true + }, + [`${this.ctx.tn_camelize}FindOne`]: { + admin: true, + user: true, + guest: true + }, + [`${this.ctx.tn_camelize}Count`]: { + admin: true, + user: true, + guest: true + }, + [`${this.ctx.tn_camelize}Distinct`]: { + admin: true, + user: true, + guest: true + }, + [`${this.ctx.tn_camelize}GroupBy`]: { + admin: true, + user: true, + guest: true + }, + [`${this.ctx.tn_camelize}Aggregate`]: { + admin: true, + user: true, + guest: true + }, + [`${this.ctx.tn_camelize}Distribution`]: { + admin: true, + user: true, + guest: true + }, + [`${this.ctx.tn_camelize}CreateBulk`]: { + admin: true, + user: true, + guest: false + }, + [`${this.ctx.tn_camelize}UpdateBulk`]: { + admin: true, + user: true, + guest: false + }, + [`${this.ctx.tn_camelize}DeleteBulk`]: { + admin: true, + user: true, + guest: false + } + }; } getFunctions() { return { - [`${this.ctx.tn_camelize}List`]: [` + [`${this.ctx.tn_camelize}List`]: [ + ` async function(args, {req,res,next}){ return (await req.model.list(args)).map(o => { return new req.gqlType(o); }); } - `], - [`${this.ctx.tn_camelize}Read`]: [` + ` + ], + [`${this.ctx.tn_camelize}Read`]: [ + ` async function(args, {req,res,next}){ const data = await req.model.readByPk(args.id); return new req.gqlType(data); } - `], - [`${this.ctx.tn_camelize}Create`]: [` + ` + ], + [`${this.ctx.tn_camelize}Create`]: [ + ` async function(args, {req,res,next}){ const data = await req.model.insert(args.data); return new req.gqlType(data); } - `], - [`${this.ctx.tn_camelize}Update`]: [` + ` + ], + [`${this.ctx.tn_camelize}Update`]: [ + ` async function(args, {req,res,next}){ const data = await req.model.updateByPk(args.id, args.data); return data; } - `], - [`${this.ctx.tn_camelize}Delete`]: [` + ` + ], + [`${this.ctx.tn_camelize}Delete`]: [ + ` async function(args, {req,res,next}){ const data = await req.model.delByPk(args.id); return data; } - `], - [`${this.ctx.tn_camelize}Exists`]: [` + ` + ], + [`${this.ctx.tn_camelize}Exists`]: [ + ` async function(args, {req,res,next}){ const data = await req.model.exists(args.id); return data; } - `], - [`${this.ctx.tn_camelize}FindOne`]: [` + ` + ], + [`${this.ctx.tn_camelize}FindOne`]: [ + ` async function(args, {req,res,next}){ const data = await req.model.findOne(args); return new req.gqlType(data); } - `], - [`${this.ctx.tn_camelize}Count`]: [` + ` + ], + [`${this.ctx.tn_camelize}Count`]: [ + ` async function(args, {req,res,next}){ const data = await req.model.countByPk(args); return data.count; } - `], - [`${this.ctx.tn_camelize}Distinct`]: [` + ` + ], + [`${this.ctx.tn_camelize}Distinct`]: [ + ` async function(args, {req,res,next}){ const data = (await req.model.distinct(args)).map(d => new req.gqlType(d)); return data; } - `], - [`${this.ctx.tn_camelize}GroupBy`]: [` + ` + ], + [`${this.ctx.tn_camelize}GroupBy`]: [ + ` async function(args, {req,res,next}){ const data = await req.model.groupBy(args); return data; } - `], - [`${this.ctx.tn_camelize}Aggregate`]: [` + ` + ], + [`${this.ctx.tn_camelize}Aggregate`]: [ + ` async function(args, {req,res,next}){ const data = await req.model.aggregate(args); return data; } - `], - [`${this.ctx.tn_camelize}Distribution`]: [` + ` + ], + [`${this.ctx.tn_camelize}Distribution`]: [ + ` async function(args, {req,res,next}){ const data = await req.model.distribution(args); return data; } - `], - [`${this.ctx.tn_camelize}CreateBulk`]: [` + ` + ], + [`${this.ctx.tn_camelize}CreateBulk`]: [ + ` async function(args, {req,res,next}){ const data = await req.model.insertb(args.data); return data; } - `], - [`${this.ctx.tn_camelize}UpdateBulk`]: [` + ` + ], + [`${this.ctx.tn_camelize}UpdateBulk`]: [ + ` async function(args, {req,res,next}){ const data = await req.model.updateb(args.data); return data; } - `], - [`${this.ctx.tn_camelize}DeleteBulk`]: [` + ` + ], + [`${this.ctx.tn_camelize}DeleteBulk`]: [ + ` async function(args, {req,res,next}){ const data = await req.model.delb(args.data); return data; } - `] - } + ` + ] + }; } - } - export default ExpressXcPolicyGql; diff --git a/packages/nocodb/src/lib/sqlMgr/code/gql-schema/xc-ts/BaseGqlXcTsSchema.ts b/packages/nocodb/src/lib/sqlMgr/code/gql-schema/xc-ts/BaseGqlXcTsSchema.ts index 7b78e26fc4..8a9d741820 100644 --- a/packages/nocodb/src/lib/sqlMgr/code/gql-schema/xc-ts/BaseGqlXcTsSchema.ts +++ b/packages/nocodb/src/lib/sqlMgr/code/gql-schema/xc-ts/BaseGqlXcTsSchema.ts @@ -1,11 +1,10 @@ -import lodash from "lodash"; +import lodash from 'lodash'; -import BaseRender from "../../BaseRender"; +import BaseRender from '../../BaseRender'; -import {AGG_DEFAULT_COLS, GROUPBY_DEFAULT_COLS} from "./schemaHelp"; +import { AGG_DEFAULT_COLS, GROUPBY_DEFAULT_COLS } from './schemaHelp'; abstract class BaseGqlXcTsSchema extends BaseRender { - /** * * @param dir @@ -15,16 +14,14 @@ abstract class BaseGqlXcTsSchema extends BaseRender { * @param ctx.columns * @param ctx.relations */ - protected constructor({dir, filename, ctx}) { - super({dir, filename, ctx}); + protected constructor({ dir, filename, ctx }) { + super({ dir, filename, ctx }); } - /** * Prepare variables used in code template */ public prepare(): any { - const data: any = {}; data.columns = { @@ -33,7 +30,6 @@ abstract class BaseGqlXcTsSchema extends BaseRender { }; return data; - } /** @@ -45,7 +41,6 @@ abstract class BaseGqlXcTsSchema extends BaseRender { * @private */ public _renderColumns(args): string { - let str = ''; str += ` @@ -53,12 +48,11 @@ abstract class BaseGqlXcTsSchema extends BaseRender { ${this._getQuery(args)},\r\n ${this._getMutation(args)}\r\n ${this._getType(args)}\r\n - ` + `; str += ''; return str; - } public getString(): string { @@ -82,138 +76,144 @@ abstract class BaseGqlXcTsSchema extends BaseRender { } const props = []; for (const v of args.v) { - if (!v.formula && !v.rl) continue - props.push(`\t\t${v._cn}: JSON`) + if (!v.formula && !v.rl) continue; + props.push(`\t\t${v._cn}: JSON`); } return props.length ? `\r\n${props.join('\r\n')}\r\n` : ''; } protected _getInputType(args): string { - let str = `input ${args._tn}Input { \r\n` + let str = `input ${args._tn}Input { \r\n`; for (const column of args.columns) { if (/\s/.test(column._cn)) { console.log(`Skipping ${args.tn}.${column._cn}`); } else { str += `\t\t${column._cn}: ${this._getGraphqlType(column)},\r\n`; } - } str += `\t}`; return str; } protected _getQuery(args): string { - let str = `type Query { \r\n` - str += `\t\t${args._tn}List(where: String,condition:Condition${args._tn}, limit: Int, offset: Int, sort: String,conditionGraph: String): [${args._tn}]\r\n` - str += `\t\t${args._tn}Read(id:String!): ${args._tn}\r\n` - str += `\t\t${args._tn}Exists(id: String!): Boolean\r\n` - str += `\t\t${args._tn}FindOne(where: String,condition:Condition${args._tn}): ${args._tn}\r\n` - str += `\t\t${args._tn}Count(where: String,condition:Condition${args._tn},conditionGraph: String): Int\r\n` - str += `\t\t${args._tn}Distinct(column_name: String, where: String,condition:Condition${args._tn}, limit: Int, offset: Int, sort: String): [${args._tn}]\r\n` - str += `\t\t${args._tn}GroupBy(fields: String, having: String, limit: Int, offset: Int, sort: String): [${args._tn}GroupBy]\r\n` - str += `\t\t${args._tn}Aggregate(column_name: String!, having: String, limit: Int, offset: Int, sort: String, func: String!): [${args._tn}Aggregate]\r\n` - str += `\t\t${args._tn}Distribution(min: Int, max: Int, step: Int, steps: String, column_name: String!): [distribution]\r\n` - str += `\t}\r\n` + let str = `type Query { \r\n`; + str += `\t\t${args._tn}List(where: String,condition:Condition${args._tn}, limit: Int, offset: Int, sort: String,conditionGraph: String): [${args._tn}]\r\n`; + str += `\t\t${args._tn}Read(id:String!): ${args._tn}\r\n`; + str += `\t\t${args._tn}Exists(id: String!): Boolean\r\n`; + str += `\t\t${args._tn}FindOne(where: String,condition:Condition${args._tn}): ${args._tn}\r\n`; + str += `\t\t${args._tn}Count(where: String,condition:Condition${args._tn},conditionGraph: String): Int\r\n`; + str += `\t\t${args._tn}Distinct(column_name: String, where: String,condition:Condition${args._tn}, limit: Int, offset: Int, sort: String): [${args._tn}]\r\n`; + str += `\t\t${args._tn}GroupBy(fields: String, having: String, limit: Int, offset: Int, sort: String): [${args._tn}GroupBy]\r\n`; + str += `\t\t${args._tn}Aggregate(column_name: String!, having: String, limit: Int, offset: Int, sort: String, func: String!): [${args._tn}Aggregate]\r\n`; + str += `\t\t${args._tn}Distribution(min: Int, max: Int, step: Int, steps: String, column_name: String!): [distribution]\r\n`; + str += `\t}\r\n`; return str; } protected _getMutation(args): string { - let str = `type Mutation { \r\n` - str += `\t\t${args._tn}Create(data:${args._tn}Input): ${args._tn}\r\n` - str += `\t\t${args._tn}Update(id:String,data:${args._tn}Input): ${args._tn}\r\n` // ${args._tn}\r\n` - str += `\t\t${args._tn}Delete(id:String): Int\r\n`// ${args._tn}\r\n` - str += `\t\t${args._tn}CreateBulk(data: [${args._tn}Input]): [Int]\r\n` - str += `\t\t${args._tn}UpdateBulk(data: [${args._tn}Input]): [Int]\r\n` - str += `\t\t${args._tn}DeleteBulk(data: [${args._tn}Input]): [Int]\r\n` - str += `\t},\r\n` + let str = `type Mutation { \r\n`; + str += `\t\t${args._tn}Create(data:${args._tn}Input): ${args._tn}\r\n`; + str += `\t\t${args._tn}Update(id:String,data:${args._tn}Input): ${args._tn}\r\n`; // ${args._tn}\r\n` + str += `\t\t${args._tn}Delete(id:String): Int\r\n`; // ${args._tn}\r\n` + str += `\t\t${args._tn}CreateBulk(data: [${args._tn}Input]): [Int]\r\n`; + str += `\t\t${args._tn}UpdateBulk(data: [${args._tn}Input]): [Int]\r\n`; + str += `\t\t${args._tn}DeleteBulk(data: [${args._tn}Input]): [Int]\r\n`; + str += `\t},\r\n`; return str; } protected _getType(args): string { - - let str = `type ${args._tn} { \r\n` - let strWhere = `input Condition${args._tn} { \r\n` + let str = `type ${args._tn} { \r\n`; + let strWhere = `input Condition${args._tn} { \r\n`; for (const column of args.columns) { if (column._cn.split(' ').length > 1) { console.log(`Skipping ${args.tn}.${column._cn}`); } else { - str += `\t\t${column._cn.replace(/ /g, '_')}: ${this._getGraphqlType(column)},\r\n`; - strWhere += `\t\t${column._cn.replace(/ /g, '_')}: ${this._getGraphqlConditionType(column)},\r\n`; + str += `\t\t${column._cn.replace(/ /g, '_')}: ${this._getGraphqlType( + column + )},\r\n`; + strWhere += `\t\t${column._cn.replace( + / /g, + '_' + )}: ${this._getGraphqlConditionType(column)},\r\n`; } - } let hasManyRelations = args.hasMany; if (hasManyRelations.length > 1) { - hasManyRelations = lodash.uniqBy(hasManyRelations, (e) => { + hasManyRelations = lodash.uniqBy(hasManyRelations, e => { return [e.tn, e.rtn].join(); }); } str += hasManyRelations.length ? `\r\n` : ``; // cityList in Country - for (const {_tn} of hasManyRelations) { + for (const { _tn } of hasManyRelations) { const childTable = _tn; str += `\t\t${childTable}List: [${childTable}]\r\n`; strWhere += `\t\t${childTable}List: Condition${childTable}\r\n`; str += `\t\t${childTable}Count: Int\r\n`; } - str += this.generateManyToManyTypeProps(args); str += this.generateVirtualTypes(args); let belongsToRelations = args.belongsTo; if (belongsToRelations.length > 1) { - belongsToRelations = lodash.uniqBy(belongsToRelations, (e) => { + belongsToRelations = lodash.uniqBy(belongsToRelations, e => { return [e.tn, e.rtn].join(); }); } str += belongsToRelations.length ? `\r\n` : ``; // Country withi city - this is reverse - for (const {_rtn} of belongsToRelations) { + for (const { _rtn } of belongsToRelations) { const parentTable = _rtn; str += `\t\t${parentTable}Read(id:String): ${parentTable}\r\n`; strWhere += `\t\t${parentTable}Read: Condition${parentTable}\r\n`; } - str += `\t}\r\n` + str += `\t}\r\n`; - const grpFields = {...GROUPBY_DEFAULT_COLS}; + const grpFields = { ...GROUPBY_DEFAULT_COLS }; - str += `type ${args._tn}GroupBy { \r\n` - for (const {_cn, ...rest} of args.columns) { + str += `type ${args._tn}GroupBy { \r\n`; + for (const { _cn, ...rest } of args.columns) { if (_cn in grpFields) { grpFields[_cn] = `\t\t# ${_cn} - clashes with column in table\r\n`; } else { - str += `\t\t${_cn.replace(/ /g, '_')}: ${this._getGraphqlType(rest)},\r\n`; + str += `\t\t${_cn.replace(/ /g, '_')}: ${this._getGraphqlType( + rest + )},\r\n`; } } str += Object.values(grpFields).join(''); - str += `\t}\r\n` + str += `\t}\r\n`; - const aggFields = {...AGG_DEFAULT_COLS}; + const aggFields = { ...AGG_DEFAULT_COLS }; - str += `type ${args._tn}Aggregate { \r\n` + str += `type ${args._tn}Aggregate { \r\n`; for (const column of args.columns) { if (column._cn in aggFields) { - aggFields[column._cn] = `\t\t# ${column._cn} - clashes with column in table\r\n`; + aggFields[ + column._cn + ] = `\t\t# ${column._cn} - clashes with column in table\r\n`; } else { - str += `\t\t${column._cn.replace(/ /g, '_')}: ${this._getGraphqlType(column)},\r\n`; + str += `\t\t${column._cn.replace(/ /g, '_')}: ${this._getGraphqlType( + column + )},\r\n`; } } str += Object.values(aggFields).join(''); - str += `\t}\r\n` + str += `\t}\r\n`; strWhere += ` _or:[Condition${args._tn}] _not:Condition${args._tn} _and:[Condition${args._tn}] - \t}\r\n` - + \t}\r\n`; return `${str}\r\n\r\n${strWhere}`; } @@ -223,5 +223,4 @@ abstract class BaseGqlXcTsSchema extends BaseRender { protected abstract _getGraphqlConditionType(columnObj): string; } - export default BaseGqlXcTsSchema; diff --git a/packages/nocodb/src/lib/sqlMgr/code/gql-schema/xc-ts/GqlXcSchemaFactory.ts b/packages/nocodb/src/lib/sqlMgr/code/gql-schema/xc-ts/GqlXcSchemaFactory.ts index 0e12f15676..7f74f964b2 100644 --- a/packages/nocodb/src/lib/sqlMgr/code/gql-schema/xc-ts/GqlXcSchemaFactory.ts +++ b/packages/nocodb/src/lib/sqlMgr/code/gql-schema/xc-ts/GqlXcSchemaFactory.ts @@ -1,25 +1,28 @@ -import GqlXcSchemaMssql from "./GqlXcTsSchemaMssql"; -import GqlXcTsSchemaMysql from "./GqlXcTsSchemaMysql"; -import GqlXcSchemaOracle from "./GqlXcTsSchemaOracle"; -import GqlXcSchemaPg from "./GqlXcTsSchemaPg"; -import GqlXcSchemaSqlite from "./GqlXcTsSchemaSqlite"; +import GqlXcSchemaMssql from './GqlXcTsSchemaMssql'; +import GqlXcTsSchemaMysql from './GqlXcTsSchemaMysql'; +import GqlXcSchemaOracle from './GqlXcTsSchemaOracle'; +import GqlXcSchemaPg from './GqlXcTsSchemaPg'; +import GqlXcSchemaSqlite from './GqlXcTsSchemaSqlite'; class GqlXcSchemaFactory { - public static create(connectionConfig, args):any { - if (connectionConfig.client === "mysql2" || connectionConfig.client === "mysql") { - return new GqlXcTsSchemaMysql(args) - } else if (connectionConfig.client === "sqlite3") { - return new GqlXcSchemaSqlite(args) - } else if (connectionConfig.client === "mssql") { + public static create(connectionConfig, args): any { + if ( + connectionConfig.client === 'mysql2' || + connectionConfig.client === 'mysql' + ) { + return new GqlXcTsSchemaMysql(args); + } else if (connectionConfig.client === 'sqlite3') { + return new GqlXcSchemaSqlite(args); + } else if (connectionConfig.client === 'mssql') { return new GqlXcSchemaMssql(args); - } else if (connectionConfig.client === "pg") { + } else if (connectionConfig.client === 'pg') { return new GqlXcSchemaPg(args); - } else if (connectionConfig.client === "oracledb") { + } else if (connectionConfig.client === 'oracledb') { return new GqlXcSchemaOracle(args); } - throw new Error("Database not supported"); + throw new Error('Database not supported'); } } -export default GqlXcSchemaFactory; \ No newline at end of file +export default GqlXcSchemaFactory; diff --git a/packages/nocodb/src/lib/sqlMgr/code/gql-schema/xc-ts/GqlXcTsSchemaMssql.ts b/packages/nocodb/src/lib/sqlMgr/code/gql-schema/xc-ts/GqlXcTsSchemaMssql.ts index 0d603dcd17..79eab476be 100644 --- a/packages/nocodb/src/lib/sqlMgr/code/gql-schema/xc-ts/GqlXcTsSchemaMssql.ts +++ b/packages/nocodb/src/lib/sqlMgr/code/gql-schema/xc-ts/GqlXcTsSchemaMssql.ts @@ -1,8 +1,6 @@ -import BaseGqlXcTsSchema from "./BaseGqlXcTsSchema"; - +import BaseGqlXcTsSchema from './BaseGqlXcTsSchema'; class GqlXcTsSchemaMssql extends BaseGqlXcTsSchema { - /** * * @param dir @@ -12,11 +10,11 @@ class GqlXcTsSchemaMssql extends BaseGqlXcTsSchema { * @param ctx.columns * @param ctx.relations */ - constructor({dir, filename, ctx}) { - super({dir, filename, ctx}); + constructor({ dir, filename, ctx }) { + super({ dir, filename, ctx }); } -/* + /* /!** * @@ -168,11 +166,8 @@ class GqlXcTsSchemaMssql extends BaseGqlXcTsSchema { } */ - _getGraphqlType(columnObj): any { - switch (columnObj.dt) { - case 'smallint': case 'int': case 'tinyint': @@ -215,44 +210,36 @@ class GqlXcTsSchemaMssql extends BaseGqlXcTsSchema { case 'xml': case 'varchar': default: - return "String"; + return 'String'; break; - case 'geography': case 'geometry': case 'json': return 'JSON'; - } - } _getGraphqlConditionType(columnObj): any { - switch (this._getGraphqlType(columnObj.dt)) { - - case "Int": - return 'ConditionInt' - case "Float": + case 'Int': + return 'ConditionInt'; + case 'Float': return 'ConditionFloat'; - case "Boolean": - return 'ConditionBoolean' - case "String": - return 'ConditionString' - case "[String]": - return 'ConditionString' - case "JSON": - return 'ConditionString' + case 'Boolean': + return 'ConditionBoolean'; + case 'String': + return 'ConditionString'; + case '[String]': + return 'ConditionString'; + case 'JSON': + return 'ConditionString'; } - } -/* getString() { + /* getString() { return this._renderColumns(this.ctx); }*/ - } - export default GqlXcTsSchemaMssql; diff --git a/packages/nocodb/src/lib/sqlMgr/code/gql-schema/xc-ts/GqlXcTsSchemaMysql.ts b/packages/nocodb/src/lib/sqlMgr/code/gql-schema/xc-ts/GqlXcTsSchemaMysql.ts index a16cfab471..b53c825798 100644 --- a/packages/nocodb/src/lib/sqlMgr/code/gql-schema/xc-ts/GqlXcTsSchemaMysql.ts +++ b/packages/nocodb/src/lib/sqlMgr/code/gql-schema/xc-ts/GqlXcTsSchemaMysql.ts @@ -1,8 +1,6 @@ -import BaseGqlXcTsSchema from "./BaseGqlXcTsSchema"; - +import BaseGqlXcTsSchema from './BaseGqlXcTsSchema'; class GqlXcTsSchemaMysql extends BaseGqlXcTsSchema { - /** * * @param dir @@ -12,8 +10,8 @@ class GqlXcTsSchemaMysql extends BaseGqlXcTsSchema { * @param ctx.columns * @param ctx.relations */ - constructor({dir, filename, ctx}) { - super({dir, filename, ctx}); + constructor({ dir, filename, ctx }) { + super({ dir, filename, ctx }); } /*/!** @@ -183,100 +181,89 @@ class GqlXcTsSchemaMysql extends BaseGqlXcTsSchema { */ protected _getGraphqlType(columnObj): string { - switch (columnObj.dt) { - - case "tinyint": - case "smallint": - case "mediumint": - case "bigint": - case "serial": - case "int": - return "Int"; + case 'tinyint': + case 'smallint': + case 'mediumint': + case 'bigint': + case 'serial': + case 'int': + return 'Int'; break; - case "float": - case "decimal": - case "double": - case "real": - return "Float"; + case 'float': + case 'decimal': + case 'double': + case 'real': + return 'Float'; break; - case "bit": - case "boolean": - return "Boolean"; + case 'bit': + case 'boolean': + return 'Boolean'; - case "date": - case "datetime": - case "timestamp": - case "time": - case "year": - return "String" + case 'date': + case 'datetime': + case 'timestamp': + case 'time': + case 'year': + return 'String'; break; - case "char": - case "varchar": - case "nchar": - case "text": - case "tinytext": - case "mediumtext": - case "longtext": - case "binary": - case "varbinary": - case "blob": - case "tinyblob": - case "mediumblob": - case "longblob": - case "enum": - return "String" + case 'char': + case 'varchar': + case 'nchar': + case 'text': + case 'tinytext': + case 'mediumtext': + case 'longtext': + case 'binary': + case 'varbinary': + case 'blob': + case 'tinyblob': + case 'mediumblob': + case 'longblob': + case 'enum': + return 'String'; break; - case "set": - return "JSON"; + case 'set': + return 'JSON'; break; - case "geometry": - case "point": - case "linestring": - case "polygon": - case "multipoint": - case "multilinestring": - case "multipolygon": - case "json": + case 'geometry': + case 'point': + case 'linestring': + case 'polygon': + case 'multipoint': + case 'multilinestring': + case 'multipolygon': + case 'json': default: - return "JSON" + return 'JSON'; break; - } - } protected _getGraphqlConditionType(columnObj): any { - switch (this._getGraphqlType(columnObj.dt)) { - - case "Int": - return 'ConditionInt' - case "Float": + case 'Int': + return 'ConditionInt'; + case 'Float': return 'ConditionFloat'; - case "Boolean": - return 'ConditionBoolean' - case "String": - case "JSON": - return 'ConditionString' - case "[String]": - return 'ConditionString' + case 'Boolean': + return 'ConditionBoolean'; + case 'String': + case 'JSON': + return 'ConditionString'; + case '[String]': + return 'ConditionString'; } - } - /*getString() { return this._renderColumns(this.ctx); }*/ - - } - export default GqlXcTsSchemaMysql; diff --git a/packages/nocodb/src/lib/sqlMgr/code/gql-schema/xc-ts/GqlXcTsSchemaOracle.ts b/packages/nocodb/src/lib/sqlMgr/code/gql-schema/xc-ts/GqlXcTsSchemaOracle.ts index d416349d7f..24df516ac7 100644 --- a/packages/nocodb/src/lib/sqlMgr/code/gql-schema/xc-ts/GqlXcTsSchemaOracle.ts +++ b/packages/nocodb/src/lib/sqlMgr/code/gql-schema/xc-ts/GqlXcTsSchemaOracle.ts @@ -1,8 +1,6 @@ -import BaseGqlXcTsSchema from "./BaseGqlXcTsSchema"; - +import BaseGqlXcTsSchema from './BaseGqlXcTsSchema'; class GqlXcSchemaOracle extends BaseGqlXcTsSchema { - /** * * @param dir @@ -12,8 +10,8 @@ class GqlXcSchemaOracle extends BaseGqlXcTsSchema { * @param ctx.columns * @param ctx.relations */ - constructor({dir, filename, ctx}) { - super({dir, filename, ctx}); + constructor({ dir, filename, ctx }) { + super({ dir, filename, ctx }); } /*/!** @@ -185,78 +183,71 @@ class GqlXcSchemaOracle extends BaseGqlXcTsSchema { } */ - protected _getGraphqlType(columnObj):any { + protected _getGraphqlType(columnObj): any { switch (columnObj.dt) { - case "char": - case "nchar": - case "nvarchar2": - case "varchar2": - case "long": - case "raw": - case "long raw": - return "String"; + case 'char': + case 'nchar': + case 'nvarchar2': + case 'varchar2': + case 'long': + case 'raw': + case 'long raw': + return 'String'; break; - case "number": - case "numeric": - case "float": - case "dec": - case "real": - case "decimal": - case "double precision": - return "Float"; + case 'number': + case 'numeric': + case 'float': + case 'dec': + case 'real': + case 'decimal': + case 'double precision': + return 'Float'; break; - case "integer": - case "int": - case "smallint": - return "Int"; + case 'integer': + case 'int': + case 'smallint': + return 'Int'; break; - case "date": - case "timestamp": - case "timestamp with time zone": - case "timestamp with local time zone": - case "interval year to month": - case "interval day to second": - case "bfile": - case "blob": - case "clob": - case "nclob": - return "String"; + case 'date': + case 'timestamp': + case 'timestamp with time zone': + case 'timestamp with local time zone': + case 'interval year to month': + case 'interval day to second': + case 'bfile': + case 'blob': + case 'clob': + case 'nclob': + return 'String'; break; - case "rowid": - case "urowid": - return "Int"; + case 'rowid': + case 'urowid': + return 'Int'; break; default: - return "String" + return 'String'; break; } - } - protected _getGraphqlConditionType(columnObj):any { - + protected _getGraphqlConditionType(columnObj): any { switch (this._getGraphqlType(columnObj.dt)) { - - case "Int": - return 'ConditionInt' - case "Float": + case 'Int': + return 'ConditionInt'; + case 'Float': return 'ConditionFloat'; - case "Boolean": - return 'ConditionBoolean' - case "String": - return 'ConditionString' - case "[String]": - return 'ConditionString' + case 'Boolean': + return 'ConditionBoolean'; + case 'String': + return 'ConditionString'; + case '[String]': + return 'ConditionString'; } - } /*getString(){ return this._renderColumns(this.ctx); }*/ - - } - export default GqlXcSchemaOracle; diff --git a/packages/nocodb/src/lib/sqlMgr/code/gql-schema/xc-ts/GqlXcTsSchemaPg.ts b/packages/nocodb/src/lib/sqlMgr/code/gql-schema/xc-ts/GqlXcTsSchemaPg.ts index 89b0c34545..51e26f1f8c 100644 --- a/packages/nocodb/src/lib/sqlMgr/code/gql-schema/xc-ts/GqlXcTsSchemaPg.ts +++ b/packages/nocodb/src/lib/sqlMgr/code/gql-schema/xc-ts/GqlXcTsSchemaPg.ts @@ -1,5 +1,4 @@ -import BaseGqlXcTsSchema from "./BaseGqlXcTsSchema"; - +import BaseGqlXcTsSchema from './BaseGqlXcTsSchema'; class GqlXcSchemaPg extends BaseGqlXcTsSchema { /** @@ -11,13 +10,11 @@ class GqlXcSchemaPg extends BaseGqlXcTsSchema { * @param ctx.columns * @param ctx.relations */ - constructor({dir, filename, ctx}) { - super({dir, filename, ctx}); + constructor({ dir, filename, ctx }) { + super({ dir, filename, ctx }); } - - -/* /!** + /* /!** * * @param args * @param args.columns @@ -42,7 +39,7 @@ class GqlXcSchemaPg extends BaseGqlXcTsSchema { }*/ -/* _getInputType(args) { + /* _getInputType(args) { let str = `input ${args.tn_camelize}Input { \r\n` for (let i = 0; i < args.columns.length; ++i) { if (args.columns[i]._cn.split(' ').length > 1) { @@ -56,7 +53,7 @@ class GqlXcSchemaPg extends BaseGqlXcTsSchema { return str; }*/ -/* _getQuery(args) { + /* _getQuery(args) { let str = `type Query { \r\n` str += `\t\t${args.tn_camelize}List(where: String,condition:Condition${args.tn_camelize}, limit: Int, offset: Int, sort: String): [${args.tn_camelize}]\r\n` str += `\t\t${args.tn_camelize}Read(id:String!): ${args.tn_camelize}\r\n` @@ -71,7 +68,7 @@ class GqlXcSchemaPg extends BaseGqlXcTsSchema { return str; }*/ -/* _getMutation(args) { + /* _getMutation(args) { let str = `type Mutation { \r\n` str += `\t\t${args.tn_camelize}Create(data:${args.tn_camelize}Input): ${args.tn_camelize}\r\n` str += `\t\t${args.tn_camelize}Update(id:String,data:${args.tn_camelize}Input): Int\r\n` //${args.tn_camelize}\r\n` @@ -83,7 +80,7 @@ class GqlXcSchemaPg extends BaseGqlXcTsSchema { return str; }*/ -/* _getType(args) { + /* _getType(args) { let str = `type ${args.tn_camelize} { \r\n` let strWhere = `input Condition${args.tn_camelize} { \r\n` @@ -170,170 +167,159 @@ class GqlXcSchemaPg extends BaseGqlXcTsSchema { }*/ _getGraphqlType(columnObj) { - switch (columnObj.dt) { - - case "int": - case "integer": - case "bigint": - case "bigserial": - case "char": - case "int2": - case "int4": - case "int8": - case "int4range": - case "int8range": - case "serial": - case "serial2": - case "smallint": - case "smallserial": - case "serial8": + case 'int': + case 'integer': + case 'bigint': + case 'bigserial': + case 'char': + case 'int2': + case 'int4': + case 'int8': + case 'int4range': + case 'int8range': + case 'serial': + case 'serial2': + case 'smallint': + case 'smallserial': + case 'serial8': if (columnObj.dtx === 'ARRAY') { - return "[Int]" + return '[Int]'; } - return "Int"; + return 'Int'; break; - case "bit": - case "bool": - case "boolean": + case 'bit': + case 'bool': + case 'boolean': if (columnObj.dtx === 'ARRAY') { - return "[Boolean]" + return '[Boolean]'; } - return "Boolean"; + return 'Boolean'; break; - case "money": - case "real": - case "float4": - case "float8": + case 'money': + case 'real': + case 'float4': + case 'float8': if (columnObj.dtx === 'ARRAY') { - return "[Float]" + return '[Float]'; } - return "Float"; + return 'Float'; break; - - case "json": - case "jsonb": - case "anyenum": - case "anynonarray": - case "path": - case "point": - case "polygon": + case 'json': + case 'jsonb': + case 'anyenum': + case 'anynonarray': + case 'path': + case 'point': + case 'polygon': if (columnObj.dtx === 'ARRAY') { - return "[JSON]" + return '[JSON]'; } return 'JSON'; - case "character": - case "uuid": - case "date": - case "double precision": - case "event_trigger": - case "fdw_handler": - case "character varying": - case "text": - case "time": - case "time without time zone": - case "timestamp": - case "timestamp without time zone": - case "timestamptz": - case "timestamp with time zone": - case "timetz": - case "time with time zone": - case "daterange": - case "gtsvector": - case "index_am_handler": - case "anyrange": - case "box": - case "bpchar": - case "bytea": - case "cid": - case "cidr": - case "circle": - case "cstring": - case "inet": - case "internal": - case "interval": - case "language_handler": - case "line": - case "lsec": - case "macaddr": - case "name": - case "numeric": - case "numrange": - case "oid": - case "opaque": - case "pg_ddl_command": - case "pg_lsn": - case "pg_node_tree": - case "record": - case "refcursor": - case "regclass": - case "regconfig": - case "regdictionary": - case "regnamespace": - case "regoper": - case "regoperator": - case "regproc": - case "regpreocedure": - case "regrole": - case "regtype": - case "reltime": - case "smgr": - case "tid": - case "tinterval": - case "trigger": - case "tsm_handler": - case "tsquery": - case "tsrange": - case "tstzrange": - case "tsvector": - case "txid_snapshot": - case "unknown": - case "void": - case "xid": - case "xml" : - default : - + case 'character': + case 'uuid': + case 'date': + case 'double precision': + case 'event_trigger': + case 'fdw_handler': + case 'character varying': + case 'text': + case 'time': + case 'time without time zone': + case 'timestamp': + case 'timestamp without time zone': + case 'timestamptz': + case 'timestamp with time zone': + case 'timetz': + case 'time with time zone': + case 'daterange': + case 'gtsvector': + case 'index_am_handler': + case 'anyrange': + case 'box': + case 'bpchar': + case 'bytea': + case 'cid': + case 'cidr': + case 'circle': + case 'cstring': + case 'inet': + case 'internal': + case 'interval': + case 'language_handler': + case 'line': + case 'lsec': + case 'macaddr': + case 'name': + case 'numeric': + case 'numrange': + case 'oid': + case 'opaque': + case 'pg_ddl_command': + case 'pg_lsn': + case 'pg_node_tree': + case 'record': + case 'refcursor': + case 'regclass': + case 'regconfig': + case 'regdictionary': + case 'regnamespace': + case 'regoper': + case 'regoperator': + case 'regproc': + case 'regpreocedure': + case 'regrole': + case 'regtype': + case 'reltime': + case 'smgr': + case 'tid': + case 'tinterval': + case 'trigger': + case 'tsm_handler': + case 'tsquery': + case 'tsrange': + case 'tstzrange': + case 'tsvector': + case 'txid_snapshot': + case 'unknown': + case 'void': + case 'xid': + case 'xml': + default: if (columnObj.dtx === 'ARRAY') { - return "[String]" + return '[String]'; } - return "String"; + return 'String'; break; - } - } - protected _getGraphqlConditionType(columnObj):any { - + protected _getGraphqlConditionType(columnObj): any { switch (this._getGraphqlType(columnObj.dt)) { - - case "Int": - return 'ConditionInt' - case "Float": + case 'Int': + return 'ConditionInt'; + case 'Float': return 'ConditionFloat'; - case "Boolean": - return 'ConditionBoolean' - case "String": - return 'ConditionString' - case "[String]": - return 'ConditionString' - case "[JSON]": - return 'ConditionString' - case "JSON": - return 'ConditionString' + case 'Boolean': + return 'ConditionBoolean'; + case 'String': + return 'ConditionString'; + case '[String]': + return 'ConditionString'; + case '[JSON]': + return 'ConditionString'; + case 'JSON': + return 'ConditionString'; } - } - /* getString(){ + /* getString(){ return this._renderColumns(this.ctx); }*/ - } - export default GqlXcSchemaPg; diff --git a/packages/nocodb/src/lib/sqlMgr/code/gql-schema/xc-ts/GqlXcTsSchemaSqlite.ts b/packages/nocodb/src/lib/sqlMgr/code/gql-schema/xc-ts/GqlXcTsSchemaSqlite.ts index 781bb8e7a5..8a555cd0be 100644 --- a/packages/nocodb/src/lib/sqlMgr/code/gql-schema/xc-ts/GqlXcTsSchemaSqlite.ts +++ b/packages/nocodb/src/lib/sqlMgr/code/gql-schema/xc-ts/GqlXcTsSchemaSqlite.ts @@ -1,8 +1,6 @@ -import BaseGqlXcTsSchema from "./BaseGqlXcTsSchema"; - +import BaseGqlXcTsSchema from './BaseGqlXcTsSchema'; class GqlXcSchemaSqlite extends BaseGqlXcTsSchema { - /** * * @param dir @@ -12,11 +10,10 @@ class GqlXcSchemaSqlite extends BaseGqlXcTsSchema { * @param ctx.columns * @param ctx.relations */ - constructor({dir, filename, ctx}) { - super({dir, filename, ctx}); + constructor({ dir, filename, ctx }) { + super({ dir, filename, ctx }); } - /*/!** * * @param args @@ -43,7 +40,6 @@ class GqlXcSchemaSqlite extends BaseGqlXcTsSchema { }*/ protected _getGraphqlType(columnObj): any { - switch (columnObj.dt) { case 'int': case 'integer': @@ -53,7 +49,7 @@ class GqlXcSchemaSqlite extends BaseGqlXcTsSchema { case 'bigint': case 'int2': case 'int8': - return 'Int' + return 'Int'; break; case 'character': @@ -67,7 +63,7 @@ class GqlXcSchemaSqlite extends BaseGqlXcTsSchema { case 'double precision': case 'float': case 'numeric': - return 'Float' + return 'Float'; break; case 'boolean': @@ -85,36 +81,30 @@ class GqlXcSchemaSqlite extends BaseGqlXcTsSchema { default: return 'String'; break; - } - } - protected _getGraphqlConditionType(columnObj): any { - switch (this._getGraphqlType(columnObj.dt)) { - - case "Int": - return 'ConditionInt' - case "Float": + case 'Int': + return 'ConditionInt'; + case 'Float': return 'ConditionFloat'; - case "Boolean": - return 'ConditionBoolean' - case "String": - return 'ConditionString' - case "[String]": - return 'ConditionString' + case 'Boolean': + return 'ConditionBoolean'; + case 'String': + return 'ConditionString'; + case '[String]': + return 'ConditionString'; } - } -/* + /* public getString(): string { return this._renderColumns(this.ctx); } */ -/* + /* protected _getInputType(args): string { let str = `input ${args.tn_camelize}Input { \r\n` for (const column of args.columns) { @@ -238,5 +228,4 @@ class GqlXcSchemaSqlite extends BaseGqlXcTsSchema { }*/ } - export default GqlXcSchemaSqlite; diff --git a/packages/nocodb/src/lib/sqlMgr/code/gql-schema/xc-ts/schemaHelp.ts b/packages/nocodb/src/lib/sqlMgr/code/gql-schema/xc-ts/schemaHelp.ts index 92c87b7897..ec13eb2a77 100644 --- a/packages/nocodb/src/lib/sqlMgr/code/gql-schema/xc-ts/schemaHelp.ts +++ b/packages/nocodb/src/lib/sqlMgr/code/gql-schema/xc-ts/schemaHelp.ts @@ -3,14 +3,11 @@ const AGG_DEFAULT_COLS = { avg: `\t\tavg: Float,\r\n`, min: `\t\tmin: Float,\r\n`, max: `\t\tmax: Int,\r\n`, - sum: `\t\tsum: Float\r\n`, + sum: `\t\tsum: Float\r\n` }; const GROUPBY_DEFAULT_COLS = { - count: `\t\tcount: Int,\r\n`, + count: `\t\tcount: Int,\r\n` }; -export { - AGG_DEFAULT_COLS, - GROUPBY_DEFAULT_COLS -} +export { AGG_DEFAULT_COLS, GROUPBY_DEFAULT_COLS }; diff --git a/packages/nocodb/src/lib/sqlMgr/code/models/xc/BaseModelXcMeta.ts b/packages/nocodb/src/lib/sqlMgr/code/models/xc/BaseModelXcMeta.ts index ba3ba6e032..6ba21252a0 100644 --- a/packages/nocodb/src/lib/sqlMgr/code/models/xc/BaseModelXcMeta.ts +++ b/packages/nocodb/src/lib/sqlMgr/code/models/xc/BaseModelXcMeta.ts @@ -1,7 +1,6 @@ -import BaseRender from "../../BaseRender"; +import BaseRender from '../../BaseRender'; abstract class BaseModelXcMeta extends BaseRender { - public abstract getXcColumnsObject(context: any): any[]; public getObject() { @@ -16,8 +15,7 @@ abstract class BaseModelXcMeta extends BaseRender { type: this.ctx.type, v: this.getVitualColumns() - } - + }; } public getVitualColumns(): any[] { @@ -29,7 +27,7 @@ abstract class BaseModelXcMeta extends BaseRender { ...(this.ctx.belongsTo || []).map(bt => ({ bt, _cn: `${bt._rtn} <= ${bt._tn}` - })), + })) ]; } @@ -38,7 +36,6 @@ abstract class BaseModelXcMeta extends BaseRender { // - /* if PK is at the end of table @@ -82,9 +79,6 @@ abstract class BaseModelXcMeta extends BaseRender { } } } - - } - export default BaseModelXcMeta; diff --git a/packages/nocodb/src/lib/sqlMgr/code/models/xc/ModelXcMetaFactory.ts b/packages/nocodb/src/lib/sqlMgr/code/models/xc/ModelXcMetaFactory.ts index 07fbe53980..62c4cfd28c 100644 --- a/packages/nocodb/src/lib/sqlMgr/code/models/xc/ModelXcMetaFactory.ts +++ b/packages/nocodb/src/lib/sqlMgr/code/models/xc/ModelXcMetaFactory.ts @@ -1,25 +1,28 @@ -import BaseModelXcMeta from "./BaseModelXcMeta"; -import ModelXcMetaMssql from "./ModelXcMetaMssql"; -import ModelXcMetaMysql from "./ModelXcMetaMysql"; -import ModelXcMetaOracle from "./ModelXcMetaOracle"; -import ModelXcMetaPg from "./ModelXcMetaPg"; -import ModelXcMetaSqlite from "./ModelXcMetaSqlite"; +import BaseModelXcMeta from './BaseModelXcMeta'; +import ModelXcMetaMssql from './ModelXcMetaMssql'; +import ModelXcMetaMysql from './ModelXcMetaMysql'; +import ModelXcMetaOracle from './ModelXcMetaOracle'; +import ModelXcMetaPg from './ModelXcMetaPg'; +import ModelXcMetaSqlite from './ModelXcMetaSqlite'; class ModelXcMetaFactory { - public static create(connectionConfig, args):BaseModelXcMeta { - if (connectionConfig.client === "mysql2" || connectionConfig.client === "mysql") { - return new ModelXcMetaMysql(args) - } else if (connectionConfig.client === "sqlite3") { - return new ModelXcMetaSqlite(args) - } else if (connectionConfig.client === "mssql") { + public static create(connectionConfig, args): BaseModelXcMeta { + if ( + connectionConfig.client === 'mysql2' || + connectionConfig.client === 'mysql' + ) { + return new ModelXcMetaMysql(args); + } else if (connectionConfig.client === 'sqlite3') { + return new ModelXcMetaSqlite(args); + } else if (connectionConfig.client === 'mssql') { return new ModelXcMetaMssql(args); - } else if (connectionConfig.client === "pg") { + } else if (connectionConfig.client === 'pg') { return new ModelXcMetaPg(args); - } else if (connectionConfig.client === "oracledb") { + } else if (connectionConfig.client === 'oracledb') { return new ModelXcMetaOracle(args); } - throw new Error("Database not supported"); + throw new Error('Database not supported'); } } diff --git a/packages/nocodb/src/lib/sqlMgr/code/models/xc/ModelXcMetaMssql.ts b/packages/nocodb/src/lib/sqlMgr/code/models/xc/ModelXcMetaMssql.ts index 8febf30b2e..1fbd4a868c 100644 --- a/packages/nocodb/src/lib/sqlMgr/code/models/xc/ModelXcMetaMssql.ts +++ b/packages/nocodb/src/lib/sqlMgr/code/models/xc/ModelXcMetaMssql.ts @@ -1,7 +1,6 @@ -import BaseModelXcMeta from "./BaseModelXcMeta"; - -class ModelXcMetaMssql extends BaseModelXcMeta{ +import BaseModelXcMeta from './BaseModelXcMeta'; +class ModelXcMetaMssql extends BaseModelXcMeta { /** * @param dir * @param filename @@ -10,17 +9,15 @@ class ModelXcMetaMssql extends BaseModelXcMeta{ * @param ctx.columns * @param ctx.relations */ - constructor({dir, filename, ctx}) { - super({dir, filename, ctx}); - + constructor({ dir, filename, ctx }) { + super({ dir, filename, ctx }); } /** * Prepare variables used in code template */ prepare() { - - const data:any = {}; + const data: any = {}; /* example of simple variable */ data.tn = this.ctx.tn; @@ -57,7 +54,6 @@ class ModelXcMetaMssql extends BaseModelXcMeta{ }; return data; - } _renderXcHasMany(args) { @@ -68,7 +64,6 @@ class ModelXcMetaMssql extends BaseModelXcMeta{ return JSON.stringify(args.belongsTo); } - /** * * @param args @@ -78,38 +73,30 @@ class ModelXcMetaMssql extends BaseModelXcMeta{ * @private */ _renderXcColumns(args) { - let str = '[\r\n'; for (let i = 0; i < args.columns.length; ++i) { - str += `{\r\n`; str += `cn: '${args.columns[i].cn}',\r\n`; str += `type: '${this._getAbstractType(args.columns[i])}',\r\n`; str += `dt: '${args.columns[i].dt}',\r\n`; - if (args.columns[i].rqd) - str += `rqd: ${args.columns[i].rqd},\r\n`; + if (args.columns[i].rqd) str += `rqd: ${args.columns[i].rqd},\r\n`; if (args.columns[i].cdf) { str += `default: "${args.columns[i].cdf}",\r\n`; str += `columnDefault: "${args.columns[i].cdf}",\r\n`; } - if (args.columns[i].un) - str += `un: ${args.columns[i].un},\r\n`; + if (args.columns[i].un) str += `un: ${args.columns[i].un},\r\n`; - if (args.columns[i].pk) - str += `pk: ${args.columns[i].pk},\r\n`; + if (args.columns[i].pk) str += `pk: ${args.columns[i].pk},\r\n`; - if (args.columns[i].ai) - str += `ai: ${args.columns[i].ai},\r\n`; + if (args.columns[i].ai) str += `ai: ${args.columns[i].ai},\r\n`; - if (args.columns[i].dtxp) - str += `dtxp: "${args.columns[i].dtxp}",\r\n`; + if (args.columns[i].dtxp) str += `dtxp: "${args.columns[i].dtxp}",\r\n`; - if (args.columns[i].dtxs) - str += `dtxs: ${args.columns[i].dtxs},\r\n`; + if (args.columns[i].dtxs) str += `dtxs: ${args.columns[i].dtxs},\r\n`; str += `validate: { func: [], @@ -117,19 +104,14 @@ class ModelXcMetaMssql extends BaseModelXcMeta{ msg: [] },`; str += `},\r\n`; - } str += ']\r\n'; return str; - } - _getAbstractType(column) { - - let str = ''; switch (column.dt) { case 'bigint': @@ -247,138 +229,133 @@ class ModelXcMetaMssql extends BaseModelXcMeta{ _sequelizeGetType(column) { let str = ''; switch (column.dt) { - case "int": + case 'int': str += `DataTypes.INTEGER(${column.dtxp})`; - if (column.un) - str += `.UNSIGNED`; + if (column.un) str += `.UNSIGNED`; break; - case "tinyint": + case 'tinyint': str += `DataTypes.INTEGER(${column.dtxp})`; - if (column.un) - str += `.UNSIGNED`; + if (column.un) str += `.UNSIGNED`; break; - case "smallint": + case 'smallint': str += `DataTypes.INTEGER(${column.dtxp})`; - if (column.un) - str += `.UNSIGNED`; + if (column.un) str += `.UNSIGNED`; break; - case "mediumint": + case 'mediumint': str += `DataTypes.INTEGER(${column.dtxp})`; - if (column.un) - str += `.UNSIGNED`; + if (column.un) str += `.UNSIGNED`; break; - case "bigint": + case 'bigint': str += `DataTypes.BIGINT`; - if (column.un) - str += `.UNSIGNED`; + if (column.un) str += `.UNSIGNED`; break; - case "float": + case 'float': str += `DataTypes.FLOAT`; break; - case "decimal": + case 'decimal': str += `DataTypes.DECIMAL`; break; - case "double": + case 'double': str += `"DOUBLE(${column.dtxp},${column.ns})"`; break; - case "real": + case 'real': str += `DataTypes.FLOAT`; break; - case "bit": + case 'bit': str += `DataTypes.BOOLEAN`; break; - case "boolean": + case 'boolean': str += `DataTypes.STRING(45)`; break; - case "serial": + case 'serial': str += `DataTypes.BIGINT`; break; - case "date": + case 'date': str += `DataTypes.DATEONLY`; break; - case "datetime": + case 'datetime': str += `DataTypes.DATE`; break; - case "timestamp": + case 'timestamp': str += `DataTypes.DATE`; break; - case "time": + case 'time': str += `DataTypes.TIME`; break; - case "year": + case 'year': str += `"YEAR"`; break; - case "char": + case 'char': str += `DataTypes.CHAR(${column.dtxp})`; break; - case "varchar": + case 'varchar': str += `DataTypes.STRING(${column.dtxp})`; break; - case "nchar": + case 'nchar': str += `DataTypes.CHAR(${column.dtxp})`; break; - case "text": + case 'text': str += `DataTypes.TEXT`; break; - case "tinytext": + case 'tinytext': str += `DataTypes.TEXT`; break; - case "mediumtext": + case 'mediumtext': str += `DataTypes.TEXT`; break; - case "longtext": + case 'longtext': str += `DataTypes.TEXT`; break; - case "binary": + case 'binary': str += `"BINARY(${column.dtxp})"`; break; - case "varbinary": + case 'varbinary': str += `"VARBINARY(${column.dtxp})"`; break; - case "blob": + case 'blob': str += `"BLOB"`; break; - case "tinyblob": + case 'tinyblob': str += `"TINYBLOB"`; break; - case "mediumblob": + case 'mediumblob': str += `"MEDIUMBLOB"`; break; - case "longblob": + case 'longblob': str += `"LONGBLOB"`; break; - case "enum": + case 'enum': str += `DataTypes.ENUM(${column.dtxp})`; break; - case "set": + case 'set': str += `"SET(${column.dtxp})"`; break; - case "geometry": + case 'geometry': str += `DataTypes.GEOMETRY`; break; - case "point": + case 'point': str += `"POINT"`; break; - case "linestring": + case 'linestring': str += `"LINESTRING"`; break; - case "polygon": + case 'polygon': str += `"POLYGON"`; break; - case "multipoint": + case 'multipoint': str += `"MULTIPOINT"`; break; - case "multilinestring": + case 'multilinestring': str += `"MULTILINESTRING"`; break; - case "multipolygon": + case 'multipolygon': str += `"MULTIPOLYGON"`; break; - case "json": + case 'json': str += `DataTypes.JSON`; break; default: @@ -391,124 +368,124 @@ class ModelXcMetaMssql extends BaseModelXcMeta{ _sequelizeGetDefault(column) { let str = ''; switch (column.dt) { - case "int": + case 'int': str += `'${column.cdf}'`; break; - case "tinyint": + case 'tinyint': str += `'${column.cdf}'`; break; - case "smallint": + case 'smallint': str += `'${column.cdf}'`; break; - case "mediumint": + case 'mediumint': str += `'${column.cdf}'`; break; - case "bigint": + case 'bigint': str += `'${column.cdf}'`; break; - case "float": + case 'float': str += `'${column.cdf}'`; break; - case "decimal": + case 'decimal': str += `'${column.cdf}'`; break; - case "double": + case 'double': str += `'${column.cdf}'`; break; - case "real": + case 'real': str += `'${column.cdf}'`; break; - case "bit": + case 'bit': str += column.cdf ? column.cdf.split('b')[1] : column.cdf; break; - case "boolean": + case 'boolean': str += column.cdf; break; - case "serial": + case 'serial': str += column.cdf; break; - case "date": + case 'date': str += `sequelize.literal('${column.cdf_sequelize}')`; break; - case "datetime": + case 'datetime': str += `sequelize.literal('${column.cdf_sequelize}')`; break; - case "timestamp": + case 'timestamp': str += `sequelize.literal('${column.cdf_sequelize}')`; break; - case "time": + case 'time': str += `'${column.cdf}'`; break; - case "year": + case 'year': str += `'${column.cdf}'`; break; - case "char": + case 'char': str += `'${column.cdf}'`; break; - case "varchar": + case 'varchar': str += `'${column.cdf}'`; break; - case "nchar": + case 'nchar': str += `'${column.cdf}'`; break; - case "text": + case 'text': str += column.cdf; break; - case "tinytext": + case 'tinytext': str += column.cdf; break; - case "mediumtext": + case 'mediumtext': str += column.cdf; break; - case "longtext": + case 'longtext': str += column.cdf; break; - case "binary": + case 'binary': str += column.cdf; break; - case "varbinary": + case 'varbinary': str += column.cdf; break; - case "blob": + case 'blob': str += column.cdf; break; - case "tinyblob": + case 'tinyblob': str += column.cdf; break; - case "mediumblob": + case 'mediumblob': str += column.cdf; break; - case "longblob": + case 'longblob': str += column.cdf; break; - case "enum": + case 'enum': str += `'${column.cdf}'`; break; - case "set": + case 'set': str += `'${column.cdf}'`; break; - case "geometry": + case 'geometry': str += `'${column.cdf}'`; break; - case "point": + case 'point': str += `'${column.cdf}'`; break; - case "linestring": + case 'linestring': str += `'${column.cdf}'`; break; - case "polygon": + case 'polygon': str += `'${column.cdf}'`; break; - case "multipoint": + case 'multipoint': str += `'${column.cdf}'`; break; - case "multilinestring": + case 'multilinestring': str += `'${column.cdf}'`; break; - case "multipolygon": + case 'multipolygon': str += `'${column.cdf}'`; break; - case "json": + case 'json': str += column.cdf; break; } @@ -516,7 +493,6 @@ class ModelXcMetaMssql extends BaseModelXcMeta{ } getXcColumnsObject(args) { - const columnsArr = []; for (const column of args.columns) { @@ -533,7 +509,7 @@ class ModelXcMetaMssql extends BaseModelXcMeta{ uidt: column.uidt || this._getUIDataType(column), uip: column.uip, uicn: column.uicn, - ...column, + ...column }; if (column.rqd) { @@ -565,7 +541,7 @@ class ModelXcMetaMssql extends BaseModelXcMeta{ columnObj.dtxs = column.dtxs; } - columnsArr.push(columnObj) + columnsArr.push(columnObj); } this.mapDefaultPrimaryValue(columnsArr); @@ -573,7 +549,7 @@ class ModelXcMetaMssql extends BaseModelXcMeta{ return columnsArr; } -/* + /* getObject() { return { tn: this.ctx.tn, @@ -589,45 +565,43 @@ class ModelXcMetaMssql extends BaseModelXcMeta{ } */ - - _getUIDataType(col):any { + _getUIDataType(col): any { switch (this.getAbstractType(col)) { case 'integer': return 'Number'; - case "boolean": + case 'boolean': return 'Checkbox'; - case 'float': + case 'float': return 'Decimal'; - case "date": + case 'date': return 'Date'; - case "datetime": + case 'datetime': return 'DateTime'; - case "time": + case 'time': return 'Time'; case 'year': return 'Year'; - case 'string': + case 'string': return 'SingleLineText'; - case "text": + case 'text': return 'LongText'; - case "enum": + case 'enum': return 'SingleSelect'; - case "set": + case 'set': return 'MultiSelect'; - case "json": + case 'json': return 'LongText'; case 'blob': - return 'LongText' + return 'LongText'; case 'geometry': - return 'Geometry' + return 'Geometry'; default: - return 'SpecificDBType' + return 'SpecificDBType'; } - } - getAbstractType(col):any { - const dt =(col.dt || col.dt).toLowerCase(); + getAbstractType(col): any { + const dt = (col.dt || col.dt).toLowerCase(); switch (dt) { case 'bigint': case 'smallint': @@ -663,12 +637,12 @@ class ModelXcMetaMssql extends BaseModelXcMeta{ case 'money': case 'nchar': - return 'string' + return 'string'; case 'ntext': return 'text'; case 'numeric': - return 'float' + return 'float'; case 'nvarchar': return 'string'; case 'real': @@ -677,15 +651,14 @@ class ModelXcMetaMssql extends BaseModelXcMeta{ case 'json': return 'json'; - case 'smallmoney': case 'sql_variant': case 'sysname': return dt; case 'text': - return 'text' + return 'text'; case 'time': - return 'time' + return 'time'; case 'timestamp': return 'timestamp'; @@ -695,12 +668,8 @@ class ModelXcMetaMssql extends BaseModelXcMeta{ return dt; case 'varchar': return 'string'; - } - } - } - export default ModelXcMetaMssql; diff --git a/packages/nocodb/src/lib/sqlMgr/code/models/xc/ModelXcMetaMysql.ts b/packages/nocodb/src/lib/sqlMgr/code/models/xc/ModelXcMetaMysql.ts index ba2235ccb5..f96c2734fb 100644 --- a/packages/nocodb/src/lib/sqlMgr/code/models/xc/ModelXcMetaMysql.ts +++ b/packages/nocodb/src/lib/sqlMgr/code/models/xc/ModelXcMetaMysql.ts @@ -1,7 +1,6 @@ -import BaseModelXcMeta from "./BaseModelXcMeta"; +import BaseModelXcMeta from './BaseModelXcMeta'; class ModelXcMetaMysql extends BaseModelXcMeta { - /** * @param dir * @param filename @@ -10,16 +9,14 @@ class ModelXcMetaMysql extends BaseModelXcMeta { * @param ctx.columns * @param ctx.relations */ - constructor({dir, filename, ctx}) { - super({dir, filename, ctx}); - + constructor({ dir, filename, ctx }) { + super({ dir, filename, ctx }); } /** * Prepare variables used in code template */ prepare() { - const data: any = {}; /* example of simple variable */ @@ -58,7 +55,6 @@ class ModelXcMetaMysql extends BaseModelXcMeta { }; return data; - } _renderXcHasMany(args) { @@ -69,7 +65,6 @@ class ModelXcMetaMysql extends BaseModelXcMeta { return JSON.stringify(args.belongsTo); } - /** * * @param args @@ -79,37 +74,29 @@ class ModelXcMetaMysql extends BaseModelXcMeta { * @private */ _renderXcColumns(args) { - let str = '[\r\n'; for (let i = 0; i < args.columns.length; ++i) { - str += `{\r\n`; str += `cn: '${args.columns[i].cn}',\r\n`; str += `type: '${this._getAbstractType(args.columns[i])}',\r\n`; str += `dt: '${args.columns[i].dt}',\r\n`; - if (args.columns[i].rqd) - str += `rqd: ${args.columns[i].rqd},\r\n`; + if (args.columns[i].rqd) str += `rqd: ${args.columns[i].rqd},\r\n`; if (args.columns[i].cdf) { str += `default: "${args.columns[i].cdf}",\r\n`; str += `columnDefault: "${args.columns[i].cdf}",\r\n`; } - if (args.columns[i].un) - str += `un: ${args.columns[i].un},\r\n`; + if (args.columns[i].un) str += `un: ${args.columns[i].un},\r\n`; - if (args.columns[i].pk) - str += `pk: ${args.columns[i].pk},\r\n`; + if (args.columns[i].pk) str += `pk: ${args.columns[i].pk},\r\n`; - if (args.columns[i].ai) - str += `ai: ${args.columns[i].ai},\r\n`; + if (args.columns[i].ai) str += `ai: ${args.columns[i].ai},\r\n`; - if (args.columns[i].dtxp) - str += `dtxp: "${args.columns[i].dtxp}",\r\n`; + if (args.columns[i].dtxp) str += `dtxp: "${args.columns[i].dtxp}",\r\n`; - if (args.columns[i].dtxs) - str += `dtxs: ${args.columns[i].dtxs},\r\n`; + if (args.columns[i].dtxs) str += `dtxs: ${args.columns[i].dtxs},\r\n`; str += `validate: { func: [], @@ -117,18 +104,14 @@ class ModelXcMetaMysql extends BaseModelXcMeta { msg: [] },`; str += `},\r\n`; - } str += ']\r\n'; return str; - } - getXcColumnsObject(args) { - const columnsArr = []; for (const column of args.columns) { @@ -146,7 +129,7 @@ class ModelXcMetaMysql extends BaseModelXcMeta { uidt: column.uidt || this._getUIDataType(column), uip: column.uip, uicn: column.uicn, - ...column, + ...column }; if (column.rqd) { @@ -178,136 +161,134 @@ class ModelXcMetaMysql extends BaseModelXcMeta { columnObj.dtxs = column.dtxs; } - columnsArr.push(columnObj) + columnsArr.push(columnObj); } this.mapDefaultPrimaryValue(columnsArr); return columnsArr; - } _getAbstractType(column) { - let str = ''; switch (column.dt) { - case "int": - str = 'integer' + case 'int': + str = 'integer'; break; - case "tinyint": - str = 'integer' + case 'tinyint': + str = 'integer'; break; - case "smallint": - str = 'integer' + case 'smallint': + str = 'integer'; break; - case "mediumint": - str = 'integer' + case 'mediumint': + str = 'integer'; break; - case "bigint": - str = 'bigInteger' + case 'bigint': + str = 'bigInteger'; break; - case "float": - str = 'float' + case 'float': + str = 'float'; break; - case "decimal": - str = 'decimal' + case 'decimal': + str = 'decimal'; break; - case "double": - str = 'double' + case 'double': + str = 'double'; break; - case "real": - str = 'real' + case 'real': + str = 'real'; break; - case "bit": - str = 'boolean' + case 'bit': + str = 'boolean'; break; - case "boolean": - str = 'boolean' + case 'boolean': + str = 'boolean'; break; - case "serial": - str = 'serial' + case 'serial': + str = 'serial'; break; - case "date": - str = 'date' + case 'date': + str = 'date'; break; - case "datetime": - str = 'datetime' + case 'datetime': + str = 'datetime'; break; - case "timestamp": - str = 'timestamp' + case 'timestamp': + str = 'timestamp'; break; - case "time": - str = 'time' + case 'time': + str = 'time'; break; - case "year": - str = 'year' + case 'year': + str = 'year'; break; - case "char": - str = 'string' + case 'char': + str = 'string'; break; - case "varchar": - str = 'string' + case 'varchar': + str = 'string'; break; - case "nchar": - str = 'string' + case 'nchar': + str = 'string'; break; - case "text": - str = 'text' + case 'text': + str = 'text'; break; - case "tinytext": - str = 'text' + case 'tinytext': + str = 'text'; break; - case "mediumtext": - str = 'text' + case 'mediumtext': + str = 'text'; break; - case "longtext": - str = 'text' + case 'longtext': + str = 'text'; break; - case "binary": - str = 'binary' + case 'binary': + str = 'binary'; break; - case "varbinary": - str = 'binary' + case 'varbinary': + str = 'binary'; break; - case "blob": - str = 'blob' + case 'blob': + str = 'blob'; break; - case "tinyblob": - str = 'tinyblob' + case 'tinyblob': + str = 'tinyblob'; break; - case "mediumblob": - str = 'mediumblob' + case 'mediumblob': + str = 'mediumblob'; break; - case "longblob": - str = 'longblob' + case 'longblob': + str = 'longblob'; break; - case "enum": - str = 'enum' + case 'enum': + str = 'enum'; break; - case "set": - str = 'set' + case 'set': + str = 'set'; break; - case "geometry": - str = 'geometry' + case 'geometry': + str = 'geometry'; break; - case "point": - str = 'point' + case 'point': + str = 'point'; break; - case "linestring": - str = 'linestring' + case 'linestring': + str = 'linestring'; break; - case "polygon": - str = 'polygon' + case 'polygon': + str = 'polygon'; break; - case "multipoint": - str = 'multipoint' + case 'multipoint': + str = 'multipoint'; break; - case "multilinestring": - str = 'multilinestring' + case 'multilinestring': + str = 'multilinestring'; break; - case "multipolygon": - str = 'multipolygon' + case 'multipolygon': + str = 'multipolygon'; break; - case "json": - str = 'json' + case 'json': + str = 'json'; break; default: str += `"${column.dt}"`; @@ -316,249 +297,240 @@ class ModelXcMetaMysql extends BaseModelXcMeta { return str; } - _getUIDataType(col): any { switch (this.getAbstractType(col)) { case 'integer': return 'Number'; - case "boolean": + case 'boolean': return 'Checkbox'; - case 'float': + case 'float': return 'Decimal'; - case "date": + case 'date': return 'Date'; - case "datetime": + case 'datetime': return 'DateTime'; - case "time": + case 'time': return 'Time'; case 'year': return 'Year'; - case 'string': + case 'string': return 'SingleLineText'; - case "text": + case 'text': return 'LongText'; - case "enum": + case 'enum': return 'SingleSelect'; - case "set": + case 'set': return 'MultiSelect'; - case "json": + case 'json': return 'LongText'; case 'blob': - return 'LongText' + return 'LongText'; case 'geometry': - return 'Geometry' + return 'Geometry'; default: - return 'SpecificDBType' + return 'SpecificDBType'; } - } - getAbstractType(col): any { switch ((col.dt || col.dt).toLowerCase()) { - case "int": - case "smallint": - case "mediumint": - case "bigint": - case "bit": + case 'int': + case 'smallint': + case 'mediumint': + case 'bigint': + case 'bit': return 'integer'; - case "boolean": + case 'boolean': return 'boolean'; - case "float": - case "decimal": - case "double": - case "serial": + case 'float': + case 'decimal': + case 'double': + case 'serial': return 'float'; - case "tinyint": + case 'tinyint': if (col.dtxp == '1') { - return 'boolean' + return 'boolean'; } else { - return 'integer' + return 'integer'; } - case "date": + case 'date': return 'date'; - case "datetime": - case "timestamp": - return 'datetime' - case "time": + case 'datetime': + case 'timestamp': + return 'datetime'; + case 'time': return 'time'; - case "year": - return 'year' - case "char": - case "varchar": - case "nchar": - return 'string' - case "text": - case "tinytext": - case "mediumtext": - case "longtext": - return 'text' + case 'year': + return 'year'; + case 'char': + case 'varchar': + case 'nchar': + return 'string'; + case 'text': + case 'tinytext': + case 'mediumtext': + case 'longtext': + return 'text'; // todo: use proper type - case "binary": + case 'binary': return 'string'; - case "varbinary": + case 'varbinary': return 'text'; - - case "blob": - case "tinyblob": - case "mediumblob": - case "longblob": + case 'blob': + case 'tinyblob': + case 'mediumblob': + case 'longblob': return 'blob'; - case "enum": - return 'enum' - case "set": + case 'enum': + return 'enum'; + case 'set': return 'set'; - case "geometry": - case "point": - case "linestring": - case "polygon": - case "multipoint": - case "multilinestring": - case "multipolygon": + case 'geometry': + case 'point': + case 'linestring': + case 'polygon': + case 'multipoint': + case 'multilinestring': + case 'multipolygon': return 'geometry'; - case "json": - return 'json' - + case 'json': + return 'json'; } } _sequelizeGetDefault(column) { let str = ''; switch (column.dt) { - case "int": + case 'int': str += `'${column.cdf}'`; break; - case "tinyint": + case 'tinyint': str += `'${column.cdf}'`; break; - case "smallint": + case 'smallint': str += `'${column.cdf}'`; break; - case "mediumint": + case 'mediumint': str += `'${column.cdf}'`; break; - case "bigint": + case 'bigint': str += `'${column.cdf}'`; break; - case "float": + case 'float': str += `'${column.cdf}'`; break; - case "decimal": + case 'decimal': str += `'${column.cdf}'`; break; - case "double": + case 'double': str += `'${column.cdf}'`; break; - case "real": + case 'real': str += `'${column.cdf}'`; break; - case "bit": + case 'bit': str += column.cdf ? column.cdf.split('b')[1] : column.cdf; break; - case "boolean": + case 'boolean': str += column.cdf; break; - case "serial": + case 'serial': str += column.cdf; break; - case "date": + case 'date': str += `sequelize.literal('${column.cdf_sequelize}')`; break; - case "datetime": + case 'datetime': str += `sequelize.literal('${column.cdf_sequelize}')`; break; - case "timestamp": + case 'timestamp': str += `sequelize.literal('${column.cdf_sequelize}')`; break; - case "time": + case 'time': str += `'${column.cdf}'`; break; - case "year": + case 'year': str += `'${column.cdf}'`; break; - case "char": + case 'char': str += `'${column.cdf}'`; break; - case "varchar": + case 'varchar': str += `'${column.cdf}'`; break; - case "nchar": + case 'nchar': str += `'${column.cdf}'`; break; - case "text": + case 'text': str += column.cdf; break; - case "tinytext": + case 'tinytext': str += column.cdf; break; - case "mediumtext": + case 'mediumtext': str += column.cdf; break; - case "longtext": + case 'longtext': str += column.cdf; break; - case "binary": + case 'binary': str += column.cdf; break; - case "varbinary": + case 'varbinary': str += column.cdf; break; - case "blob": + case 'blob': str += column.cdf; break; - case "tinyblob": + case 'tinyblob': str += column.cdf; break; - case "mediumblob": + case 'mediumblob': str += column.cdf; break; - case "longblob": + case 'longblob': str += column.cdf; break; - case "enum": + case 'enum': str += `'${column.cdf}'`; break; - case "set": + case 'set': str += `'${column.cdf}'`; break; - case "geometry": + case 'geometry': str += `'${column.cdf}'`; break; - case "point": + case 'point': str += `'${column.cdf}'`; break; - case "linestring": + case 'linestring': str += `'${column.cdf}'`; break; - case "polygon": + case 'polygon': str += `'${column.cdf}'`; break; - case "multipoint": + case 'multipoint': str += `'${column.cdf}'`; break; - case "multilinestring": + case 'multilinestring': str += `'${column.cdf}'`; break; - case "multipolygon": + case 'multipolygon': str += `'${column.cdf}'`; break; - case "json": + case 'json': str += column.cdf; break; } return str; } - - - - } export default ModelXcMetaMysql; diff --git a/packages/nocodb/src/lib/sqlMgr/code/models/xc/ModelXcMetaOracle.ts b/packages/nocodb/src/lib/sqlMgr/code/models/xc/ModelXcMetaOracle.ts index c64e715a2f..db68d90c03 100644 --- a/packages/nocodb/src/lib/sqlMgr/code/models/xc/ModelXcMetaOracle.ts +++ b/packages/nocodb/src/lib/sqlMgr/code/models/xc/ModelXcMetaOracle.ts @@ -1,7 +1,6 @@ -import BaseModelXcMeta from "./BaseModelXcMeta"; +import BaseModelXcMeta from './BaseModelXcMeta'; class ModelXcMetaOracle extends BaseModelXcMeta { - /** * @param dir * @param filename @@ -10,17 +9,15 @@ class ModelXcMetaOracle extends BaseModelXcMeta { * @param ctx.columns * @param ctx.relations */ - constructor({dir, filename, ctx}) { - super({dir, filename, ctx}); - + constructor({ dir, filename, ctx }) { + super({ dir, filename, ctx }); } /** * Prepare variables used in code template */ prepare() { - - const data:any = {}; + const data: any = {}; /* example of simple variable */ data.tn = this.ctx.tn; @@ -58,10 +55,8 @@ class ModelXcMetaOracle extends BaseModelXcMeta { }; return data; - } - _renderXcHasMany(args) { return JSON.stringify(args.hasMany); } @@ -70,7 +65,6 @@ class ModelXcMetaOracle extends BaseModelXcMeta { return JSON.stringify(args.belongsTo); } - /** * * @param args @@ -80,37 +74,29 @@ class ModelXcMetaOracle extends BaseModelXcMeta { * @private */ _renderXcColumns(args) { - let str = '[\r\n'; for (let i = 0; i < args.columns.length; ++i) { - str += `{\r\n`; str += `cn: '${args.columns[i].cn}',\r\n`; str += `type: '${this._getAbstractType(args.columns[i])}',\r\n`; str += `dt: '${args.columns[i].dt}',\r\n`; - if (args.columns[i].rqd) - str += `rqd: ${args.columns[i].rqd},\r\n`; + if (args.columns[i].rqd) str += `rqd: ${args.columns[i].rqd},\r\n`; if (args.columns[i].cdf) { str += `default: "${args.columns[i].cdf}",\r\n`; str += `columnDefault: "${args.columns[i].cdf}",\r\n`; } - if (args.columns[i].un) - str += `un: ${args.columns[i].un},\r\n`; + if (args.columns[i].un) str += `un: ${args.columns[i].un},\r\n`; - if (args.columns[i].pk) - str += `pk: ${args.columns[i].pk},\r\n`; + if (args.columns[i].pk) str += `pk: ${args.columns[i].pk},\r\n`; - if (args.columns[i].ai) - str += `ai: ${args.columns[i].ai},\r\n`; + if (args.columns[i].ai) str += `ai: ${args.columns[i].ai},\r\n`; - if (args.columns[i].dtxp) - str += `dtxp: "${args.columns[i].dtxp}",\r\n`; + if (args.columns[i].dtxp) str += `dtxp: "${args.columns[i].dtxp}",\r\n`; - if (args.columns[i].dtxs) - str += `dtxs: ${args.columns[i].dtxs},\r\n`; + if (args.columns[i].dtxs) str += `dtxs: ${args.columns[i].dtxs},\r\n`; str += `validate: { func: [], @@ -118,18 +104,14 @@ class ModelXcMetaOracle extends BaseModelXcMeta { msg: [] },`; str += `},\r\n`; - } str += ']\r\n'; return str; - } - getXcColumnsObject(args) { - const columnsArr = []; for (const column of args.columns) { @@ -140,13 +122,13 @@ class ModelXcMetaOracle extends BaseModelXcMeta { msg: [] }, cn: column.cn, - _cn: column._cn || column.cn, + _cn: column._cn || column.cn, type: this._getAbstractType(column), dt: column.dt, uidt: column.uidt || this._getUIDataType(column), uip: column.uip, uicn: column.uicn, - ...column, + ...column }; if (column.rqd) { @@ -178,108 +160,104 @@ class ModelXcMetaOracle extends BaseModelXcMeta { columnObj.dtxs = column.dtxs; } - columnsArr.push(columnObj) + columnsArr.push(columnObj); } - this.mapDefaultPrimaryValue(columnsArr); return columnsArr; - } - _getAbstractType(column) { - let str = ''; switch (column.dt) { - case "char": - str = 'string' + case 'char': + str = 'string'; break; - case "nchar": - str = 'string' + case 'nchar': + str = 'string'; break; - case "nvarchar2": - str = 'string' + case 'nvarchar2': + str = 'string'; break; - case "varchar2": - str = 'string' + case 'varchar2': + str = 'string'; break; - case "long": - str = 'string' + case 'long': + str = 'string'; break; - case "raw": + case 'raw': // todo: map correct datatype - str = 'string' + str = 'string'; break; - case "long raw": + case 'long raw': // todo: map correct datatype - str = 'string' + str = 'string'; break; - case "number": - str = 'float' + case 'number': + str = 'float'; break; - case "numeric": - str = 'float' + case 'numeric': + str = 'float'; break; - case "float": - str = 'float' + case 'float': + str = 'float'; break; - case "dec": - str = 'float' + case 'dec': + str = 'float'; break; - case "decimal": - str = 'float' + case 'decimal': + str = 'float'; break; - case "integer": - str = 'integer' + case 'integer': + str = 'integer'; break; - case "int": - str = 'integer' + case 'int': + str = 'integer'; break; - case "smallint": - str = 'integer' + case 'smallint': + str = 'integer'; break; - case "real": - str = 'float' + case 'real': + str = 'float'; break; - case "double precision": - str = 'float' + case 'double precision': + str = 'float'; break; - case "date": - str = 'date' + case 'date': + str = 'date'; break; - case "timestamp": - str = 'timestamp' + case 'timestamp': + str = 'timestamp'; break; - case "timestamp with time zone": - str = 'timestamp' + case 'timestamp with time zone': + str = 'timestamp'; break; - case "timestamp with local time zone": - str = 'timestamp' + case 'timestamp with local time zone': + str = 'timestamp'; break; - case "interval year to month": - str = "string"; + case 'interval year to month': + str = 'string'; break; - case "interval day to second": - str = "string"; + case 'interval day to second': + str = 'string'; break; - case "bfile": - str = "blob"; + case 'bfile': + str = 'blob'; break; - case "blob": - str = "blob"; + case 'blob': + str = 'blob'; break; - case "clob": - str = "string"; + case 'clob': + str = 'string'; break; - case "nclob": - str = "string"; + case 'nclob': + str = 'string'; break; - case "rowid": - str = "bigInteger"; + case 'rowid': + str = 'bigInteger'; break; - case "urowid": - str = "bigInteger"; + case 'urowid': + str = 'bigInteger'; break; default: str += `"${column.dt}"`; @@ -290,8 +268,7 @@ class ModelXcMetaOracle extends BaseModelXcMeta { return str; } - - getAbstractType(col):any { + getAbstractType(col): any { switch ((col.dt || col.dt).toLowerCase()) { case 'integer': return 'integer'; @@ -308,225 +285,223 @@ class ModelXcMetaOracle extends BaseModelXcMeta { case 'clob': case 'content pointer': case 'contigous array': - return 'string' + return 'string'; case 'date': return 'date'; case 'decimal': case 'double precision': case 'float': - return 'float' + return 'float'; case 'interval day to second': case 'interval year to month': - return 'string' + return 'string'; case 'lob pointer': - return 'string' + return 'string'; case 'long': - return 'integer' + return 'integer'; case 'long raw': - return 'string' + return 'string'; case 'named collection': case 'named object': case 'nchar': case 'nclob': - return 'string' + return 'string'; case 'nvarchar2': case 'oid': case 'pointer': case 'raw': - return 'string' + return 'string'; case 'real': case 'number': - return 'float' + return 'float'; case 'ref': case 'ref cursor': case 'rowid': case 'signed binary integer': - return 'string' + return 'string'; case 'smallint': - return 'integer' + return 'integer'; case 'table': - return 'string' + return 'string'; case 'time': case 'time with tz': - return 'time' + return 'time'; case 'timestamp': case 'timestamp with local time zone': case 'timestamp with local tz': case 'timestamp with timezone': case 'timestamp with tz': - return 'datetime' + return 'datetime'; case 'unsigned binary integer': case 'urowid': case 'varchar': case 'varchar2': - return 'string' + return 'string'; case 'varray': case 'varying array': - return 'string' + return 'string'; } } - - _getUIDataType(col):any { + _getUIDataType(col): any { switch (this.getAbstractType(col)) { case 'integer': return 'Number'; - case "boolean": + case 'boolean': return 'Checkbox'; - case 'float': + case 'float': return 'Decimal'; - case "date": + case 'date': return 'Date'; - case "datetime": + case 'datetime': return 'DateTime'; - case "time": + case 'time': return 'Time'; case 'year': return 'Year'; - case 'string': + case 'string': return 'SingleLineText'; - case "text": + case 'text': return 'LongText'; case 'blob': - return 'Attachment' - case "enum": + return 'Attachment'; + case 'enum': return 'SingleSelect'; - case "set": + case 'set': return 'MultiSelect'; - case "json": + case 'json': return 'LongText'; } - } _sequelizeGetDefault(column) { let str = ''; switch (column.dt) { - case "int": + case 'int': str += `'${column.cdf}'`; break; - case "tinyint": + case 'tinyint': str += `'${column.cdf}'`; break; - case "smallint": + case 'smallint': str += `'${column.cdf}'`; break; - case "mediumint": + case 'mediumint': str += `'${column.cdf}'`; break; - case "bigint": + case 'bigint': str += `'${column.cdf}'`; break; - case "float": + case 'float': str += `'${column.cdf}'`; break; - case "decimal": + case 'decimal': str += `'${column.cdf}'`; break; - case "double": + case 'double': str += `'${column.cdf}'`; break; - case "real": + case 'real': str += `'${column.cdf}'`; break; - case "bit": + case 'bit': str += column.cdf ? column.cdf.split('b')[1] : column.cdf; break; - case "boolean": + case 'boolean': str += column.cdf; break; - case "serial": + case 'serial': str += column.cdf; break; - case "date": + case 'date': str += `sequelize.literal('${column.cdf_sequelize}')`; break; - case "datetime": + case 'datetime': str += `sequelize.literal('${column.cdf_sequelize}')`; break; - case "timestamp": + case 'timestamp': str += `sequelize.literal('${column.cdf_sequelize}')`; break; - case "time": + case 'time': str += `'${column.cdf}'`; break; - case "year": + case 'year': str += `'${column.cdf}'`; break; - case "char": + case 'char': str += `'${column.cdf}'`; break; - case "varchar": + case 'varchar': str += `'${column.cdf}'`; break; - case "nchar": + case 'nchar': str += `'${column.cdf}'`; break; - case "text": + case 'text': str += column.cdf; break; - case "tinytext": + case 'tinytext': str += column.cdf; break; - case "mediumtext": + case 'mediumtext': str += column.cdf; break; - case "longtext": + case 'longtext': str += column.cdf; break; - case "binary": + case 'binary': str += column.cdf; break; - case "varbinary": + case 'varbinary': str += column.cdf; break; - case "blob": + case 'blob': str += column.cdf; break; - case "tinyblob": + case 'tinyblob': str += column.cdf; break; - case "mediumblob": + case 'mediumblob': str += column.cdf; break; - case "longblob": + case 'longblob': str += column.cdf; break; - case "enum": + case 'enum': str += `'${column.cdf}'`; break; - case "set": + case 'set': str += `'${column.cdf}'`; break; - case "geometry": + case 'geometry': str += `'${column.cdf}'`; break; - case "point": + case 'point': str += `'${column.cdf}'`; break; - case "linestring": + case 'linestring': str += `'${column.cdf}'`; break; - case "polygon": + case 'polygon': str += `'${column.cdf}'`; break; - case "multipoint": + case 'multipoint': str += `'${column.cdf}'`; break; - case "multilinestring": + case 'multilinestring': str += `'${column.cdf}'`; break; - case "multipolygon": + case 'multipolygon': str += `'${column.cdf}'`; break; - case "json": + case 'json': str += column.cdf; break; } return str; } -/* + /* getObject() { return { tn: this.ctx.tn, @@ -540,7 +515,6 @@ class ModelXcMetaOracle extends BaseModelXcMeta { } }*/ - } export default ModelXcMetaOracle; diff --git a/packages/nocodb/src/lib/sqlMgr/code/models/xc/ModelXcMetaPg.ts b/packages/nocodb/src/lib/sqlMgr/code/models/xc/ModelXcMetaPg.ts index bfb15698d5..307910cc9a 100644 --- a/packages/nocodb/src/lib/sqlMgr/code/models/xc/ModelXcMetaPg.ts +++ b/packages/nocodb/src/lib/sqlMgr/code/models/xc/ModelXcMetaPg.ts @@ -1,7 +1,6 @@ -import BaseModelXcMeta from "./BaseModelXcMeta"; +import BaseModelXcMeta from './BaseModelXcMeta'; class ModelXcMetaPg extends BaseModelXcMeta { - /** * @param dir * @param filename @@ -10,16 +9,14 @@ class ModelXcMetaPg extends BaseModelXcMeta { * @param ctx.columns * @param ctx.relations */ - constructor({dir, filename, ctx}) { - super({dir, filename, ctx}); - + constructor({ dir, filename, ctx }) { + super({ dir, filename, ctx }); } /** * Prepare variables used in code template */ prepare() { - const data: any = {}; /* example of simple variable */ @@ -57,10 +54,8 @@ class ModelXcMetaPg extends BaseModelXcMeta { }; return data; - } - _renderXcHasMany(args) { return JSON.stringify(args.hasMany); } @@ -69,7 +64,6 @@ class ModelXcMetaPg extends BaseModelXcMeta { return JSON.stringify(args.belongsTo); } - /** * * @param args @@ -79,38 +73,29 @@ class ModelXcMetaPg extends BaseModelXcMeta { * @private */ _renderXcColumns(args) { - let str = '[\r\n'; for (let i = 0; i < args.columns.length; ++i) { - str += `{\r\n`; str += `cn: '${args.columns[i].cn}',\r\n`; str += `type: '${this._getAbstractType(args.columns[i])}',\r\n`; str += `dt: '${args.columns[i].dt}',\r\n`; - if (args.columns[i].rqd) - str += `rqd: ${args.columns[i].rqd},\r\n`; + if (args.columns[i].rqd) str += `rqd: ${args.columns[i].rqd},\r\n`; if (args.columns[i].cdf) { str += `default: "${args.columns[i].cdf}",\r\n`; str += `columnDefault: "${args.columns[i].cdf}",\r\n`; } - if (args.columns[i].un) - str += `un: ${args.columns[i].un},\r\n`; - - if (args.columns[i].pk) - str += `pk: ${args.columns[i].pk},\r\n`; + if (args.columns[i].un) str += `un: ${args.columns[i].un},\r\n`; - if (args.columns[i].ai) - str += `ai: ${args.columns[i].ai},\r\n`; + if (args.columns[i].pk) str += `pk: ${args.columns[i].pk},\r\n`; - if (args.columns[i].dtxp) - str += `dtxp: "${args.columns[i].dtxp}",\r\n`; + if (args.columns[i].ai) str += `ai: ${args.columns[i].ai},\r\n`; - if (args.columns[i].dtxs) - str += `dtxs: ${args.columns[i].dtxs},\r\n`; + if (args.columns[i].dtxp) str += `dtxp: "${args.columns[i].dtxp}",\r\n`; + if (args.columns[i].dtxs) str += `dtxs: ${args.columns[i].dtxs},\r\n`; str += `validate: { func: [], @@ -118,310 +103,308 @@ class ModelXcMetaPg extends BaseModelXcMeta { msg: [] },`; str += `},\r\n`; - } str += ']\r\n'; return str; - } _getAbstractType(column) { let str = ''; switch (column.dt) { - case "int": + case 'int': str = 'integer'; break; - case "integer": + case 'integer': str = 'integer'; break; - case "bigint": + case 'bigint': str = 'bigInteger'; break; - case "bigserial": + case 'bigserial': str = 'bigserial'; break; - case "char": + case 'char': str = 'string'; break; - case "int2": + case 'int2': str = 'integer'; break; - case "int4": + case 'int4': str = 'integer'; break; - case "int8": + case 'int8': str = 'integer'; break; - case "int4range": + case 'int4range': str = 'int4range'; break; - case "int8range": + case 'int8range': str = 'int8range'; break; - case "serial": + case 'serial': str = 'serial'; break; - case "serial2": + case 'serial2': str = 'serial2'; break; - case "serial8": + case 'serial8': str = 'serial8'; break; - case "character": + case 'character': str = 'string'; break; - case "bit": + case 'bit': str = 'bit'; break; - case "bool": + case 'bool': str = 'boolean'; break; - case "boolean": + case 'boolean': str = 'boolean'; break; - case "date": + case 'date': str = 'date'; break; - case "double precision": + case 'double precision': str = 'double'; break; - case "event_trigger": + case 'event_trigger': str = 'event_trigger'; break; - case "fdw_handler": + case 'fdw_handler': str = 'fdw_handler'; break; - case "float4": + case 'float4': str = 'float'; break; - case "float8": + case 'float8': str = 'float'; break; - case "uuid": + case 'uuid': str = 'uuid'; break; - case "smallint": + case 'smallint': str = 'integer'; break; - case "smallserial": + case 'smallserial': str = 'smallserial'; break; - case "character varying": + case 'character varying': str = 'string'; break; - case "text": + case 'text': str = 'text'; break; - case "real": + case 'real': str = 'float'; break; - case "time": + case 'time': str = 'time'; break; - case "time without time zone": + case 'time without time zone': str = 'time'; break; - case "timestamp": + case 'timestamp': str = 'timestamp'; break; - case "timestamp without time zone": + case 'timestamp without time zone': str = 'timestamp'; break; - case "timestamptz": + case 'timestamptz': str = 'timestampt'; break; - case "timestamp with time zone": + case 'timestamp with time zone': str = 'timestamp'; break; - case "timetz": + case 'timetz': str = 'time'; break; - case "time with time zone": + case 'time with time zone': str = 'time'; break; - case "daterange": + case 'daterange': str = 'daterange'; break; - case "json": + case 'json': str = 'json'; break; - case "jsonb": + case 'jsonb': str = 'jsonb'; break; - case "gtsvector": + case 'gtsvector': str = 'gtsvector'; break; - case "index_am_handler": + case 'index_am_handler': str = 'index_am_handler'; break; - case "anyenum": + case 'anyenum': str = 'enum'; break; - case "anynonarray": + case 'anynonarray': str = 'anynonarray'; break; - case "anyrange": + case 'anyrange': str = 'anyrange'; break; - case "box": + case 'box': str = 'box'; break; - case "bpchar": + case 'bpchar': str = 'bpchar'; break; - case "bytea": + case 'bytea': str = 'bytea'; break; - case "cid": + case 'cid': str = 'cid'; break; - case "cidr": + case 'cidr': str = 'cidr'; break; - case "circle": + case 'circle': str = 'circle'; break; - case "cstring": + case 'cstring': str = 'cstring'; break; - case "inet": + case 'inet': str = 'inet'; break; - case "internal": + case 'internal': str = 'internal'; break; - case "interval": + case 'interval': str = 'interval'; break; - case "language_handler": + case 'language_handler': str = 'language_handler'; break; - case "line": + case 'line': str = 'line'; break; - case "lsec": + case 'lsec': str = 'lsec'; break; - case "macaddr": + case 'macaddr': str = 'macaddr'; break; - case "money": + case 'money': str = 'float'; break; - case "name": + case 'name': str = 'name'; break; - case "numeric": + case 'numeric': str = 'numeric'; break; - case "numrange": + case 'numrange': str = 'numrange'; break; - case "oid": + case 'oid': str = 'oid'; break; - case "opaque": + case 'opaque': str = 'opaque'; break; - case "path": + case 'path': str = 'path'; break; - case "pg_ddl_command": + case 'pg_ddl_command': str = 'pg_ddl_command'; break; - case "pg_lsn": + case 'pg_lsn': str = 'pg_lsn'; break; - case "pg_node_tree": + case 'pg_node_tree': str = 'pg_node_tree'; break; - case "point": + case 'point': str = 'point'; break; - case "polygon": + case 'polygon': str = 'polygon'; break; - case "record": + case 'record': str = 'record'; break; - case "refcursor": + case 'refcursor': str = 'refcursor'; break; - case "regclass": + case 'regclass': str = 'regclass'; break; - case "regconfig": + case 'regconfig': str = 'regconfig'; break; - case "regdictionary": + case 'regdictionary': str = 'regdictionary'; break; - case "regnamespace": + case 'regnamespace': str = 'regnamespace'; break; - case "regoper": + case 'regoper': str = 'regoper'; break; - case "regoperator": + case 'regoperator': str = 'regoperator'; break; - case "regproc": + case 'regproc': str = 'regproc'; break; - case "regpreocedure": + case 'regpreocedure': str = 'regpreocedure'; break; - case "regrole": + case 'regrole': str = 'regrole'; break; - case "regtype": + case 'regtype': str = 'regtype'; break; - case "reltime": + case 'reltime': str = 'reltime'; break; - case "smgr": + case 'smgr': str = 'smgr'; break; - case "tid": + case 'tid': str = 'tid'; break; - case "tinterval": + case 'tinterval': str = 'tinterval'; break; - case "trigger": + case 'trigger': str = 'trigger'; break; - case "tsm_handler": + case 'tsm_handler': str = 'tsm_handler'; break; - case "tsquery": + case 'tsquery': str = 'tsquery'; break; - case "tsrange": + case 'tsrange': str = 'tsrange'; break; - case "tstzrange": + case 'tstzrange': str = 'tstzrange'; break; - case "tsvector": + case 'tsvector': str = 'tsvector'; break; - case "txid_snapshot": + case 'txid_snapshot': str = 'txid_snapshot'; break; - case "unknown": + case 'unknown': str = 'unknown'; break; - case "void": + case 'void': str = 'void'; break; - case "xid": + case 'xid': str = 'xid'; break; - case "xml": + case 'xml': str = 'xml'; break; default: @@ -431,228 +414,224 @@ class ModelXcMetaPg extends BaseModelXcMeta { return str; } - _getUIDataType(col): any { switch (this.getAbstractType(col)) { case 'integer': return 'Number'; - case "boolean": + case 'boolean': return 'Checkbox'; - case 'float': + case 'float': return 'Decimal'; - case "date": + case 'date': return 'Date'; - case "datetime": + case 'datetime': return 'DateTime'; - case "time": + case 'time': return 'Time'; case 'year': return 'Year'; - case 'string': + case 'string': return 'SingleLineText'; - case "text": + case 'text': return 'LongText'; - case "enum": + case 'enum': return 'SingleSelect'; - case "set": + case 'set': return 'MultiSelect'; - case "json": + case 'json': return 'LongText'; case 'blob': - return 'LongText' + return 'LongText'; case 'geometry': - return 'Geometry' + return 'Geometry'; default: - return 'SpecificDBType' + return 'SpecificDBType'; } - } - getAbstractType(col): any { const dt = col.dt.toLowerCase(); switch (dt) { - case "anyenum": + case 'anyenum': return 'enum'; - case "anynonarray": - case "anyrange": + case 'anynonarray': + case 'anyrange': return dt; - case "bit": + case 'bit': return 'integer'; - case "bigint": - case "bigserial": + case 'bigint': + case 'bigserial': return 'integer'; - case "bool": + case 'bool': return 'boolean'; - case "bpchar": - case "bytea": + case 'bpchar': + case 'bytea': return dt; - case "char": - case "character": - case "character varying": + case 'char': + case 'character': + case 'character varying': return 'string'; - case "cid": - case "cidr": - case "cstring": + case 'cid': + case 'cidr': + case 'cstring': return dt; - case "date": - return 'date' - case "daterange": - return 'string' - case "double precision": + case 'date': + return 'date'; + case 'daterange': + return 'string'; + case 'double precision': return 'string'; - case "event_trigger": - case "fdw_handler": + case 'event_trigger': + case 'fdw_handler': return dt; - case "float4": - case "float8": + case 'float4': + case 'float8': return 'float'; - case "gtsvector": - case "index_am_handler": - case "inet": + case 'gtsvector': + case 'index_am_handler': + case 'inet': return dt; - case "int": - case "int2": - case "int4": - case "int8": - case "integer": - return 'integer' - case "int4range": - case "int8range": - case "internal": - case "interval": - return 'string' - case "json": - return 'json' - case "jsonb": + case 'int': + case 'int2': + case 'int4': + case 'int8': + case 'integer': + return 'integer'; + case 'int4range': + case 'int8range': + case 'internal': + case 'interval': + return 'string'; + case 'json': + return 'json'; + case 'jsonb': return 'string'; - case "language_handler": - case "lsec": - case "macaddr": - case "money": - case "name": - case "numeric": - case "numrange": - case "oid": - case "opaque": - case "path": - case "pg_ddl_command": - case "pg_lsn": - case "pg_node_tree": + case 'language_handler': + case 'lsec': + case 'macaddr': + case 'money': + case 'name': + case 'numeric': + case 'numrange': + case 'oid': + case 'opaque': + case 'path': + case 'pg_ddl_command': + case 'pg_lsn': + case 'pg_node_tree': return dt; - case "real": + case 'real': return 'float'; - case "record": - case "refcursor": - case "regclass": - case "regconfig": - case "regdictionary": - case "regnamespace": - case "regoper": - case "regoperator": - case "regproc": - case "regpreocedure": - case "regrole": - case "regtype": - case "reltime": + case 'record': + case 'refcursor': + case 'regclass': + case 'regconfig': + case 'regdictionary': + case 'regnamespace': + case 'regoper': + case 'regoperator': + case 'regproc': + case 'regpreocedure': + case 'regrole': + case 'regtype': + case 'reltime': return dt; - case "serial": - case "serial2": - case "serial8": - case "smallint": - case "smallserial": - return 'integer' - case "smgr": + case 'serial': + case 'serial2': + case 'serial8': + case 'smallint': + case 'smallserial': + return 'integer'; + case 'smgr': return dt; - case "text": - return 'text' - case "tid": + case 'text': + return 'text'; + case 'tid': return dt; - case "time": - case "time without time zone": - return 'time' - case "timestamp": - case "timestamp without time zone": - case "timestamptz": - case "timestamp with time zone": - return 'datetime' - case "timetz": - case "time with time zone": - return 'time' - - case "tinterval": - case "trigger": - case "tsm_handler": - case "tsquery": - case "tsrange": - case "tstzrange": - case "tsvector": - case "txid_snapshot": - case "unknown": - case "void": - case "xid": - case "xml": + case 'time': + case 'time without time zone': + return 'time'; + case 'timestamp': + case 'timestamp without time zone': + case 'timestamptz': + case 'timestamp with time zone': + return 'datetime'; + case 'timetz': + case 'time with time zone': + return 'time'; + + case 'tinterval': + case 'trigger': + case 'tsm_handler': + case 'tsquery': + case 'tsrange': + case 'tstzrange': + case 'tsvector': + case 'txid_snapshot': + case 'unknown': + case 'void': + case 'xid': + case 'xml': return dt; - case "tinyint": - case "mediumint": - return 'integer' - - case "float": - case "decimal": - case "double": - return 'float' - case "boolean": - return 'boolean' - case "datetime": - return 'datetime' - - case "uuid": - case "year": - case "varchar": - case "nchar": - return 'string' - - case "tinytext": - case "mediumtext": - case "longtext": - return 'text' - - case "binary": - case "varbinary": - return 'text' - - case "blob": - case "tinyblob": - case "mediumblob": - case "longblob": - return 'blob' - case "enum": - return 'enum' - case "set": - return 'set' - - - case "line": - case "point": - case "polygon": - case "circle": - case "box": - case "geometry": - case "linestring": - case "multipoint": - case "multilinestring": - case "multipolygon": + case 'tinyint': + case 'mediumint': + return 'integer'; + + case 'float': + case 'decimal': + case 'double': + return 'float'; + case 'boolean': + return 'boolean'; + case 'datetime': + return 'datetime'; + + case 'uuid': + case 'year': + case 'varchar': + case 'nchar': + return 'string'; + + case 'tinytext': + case 'mediumtext': + case 'longtext': + return 'text'; + + case 'binary': + case 'varbinary': + return 'text'; + + case 'blob': + case 'tinyblob': + case 'mediumblob': + case 'longblob': + return 'blob'; + case 'enum': + return 'enum'; + case 'set': + return 'set'; + + case 'line': + case 'point': + case 'polygon': + case 'circle': + case 'box': + case 'geometry': + case 'linestring': + case 'multipoint': + case 'multilinestring': + case 'multipolygon': return 'geometry'; } } @@ -660,138 +639,133 @@ class ModelXcMetaPg extends BaseModelXcMeta { _sequelizeGetType(column) { let str = ''; switch (column.dt) { - case "int": + case 'int': str += `DataTypes.INTEGER(${column.dtxp})`; - if (column.un) - str += `.UNSIGNED`; + if (column.un) str += `.UNSIGNED`; break; - case "tinyint": + case 'tinyint': str += `DataTypes.INTEGER(${column.dtxp})`; - if (column.un) - str += `.UNSIGNED`; + if (column.un) str += `.UNSIGNED`; break; - case "smallint": + case 'smallint': str += `DataTypes.INTEGER(${column.dtxp})`; - if (column.un) - str += `.UNSIGNED`; + if (column.un) str += `.UNSIGNED`; break; - case "mediumint": + case 'mediumint': str += `DataTypes.INTEGER(${column.dtxp})`; - if (column.un) - str += `.UNSIGNED`; + if (column.un) str += `.UNSIGNED`; break; - case "bigint": + case 'bigint': str += `DataTypes.BIGINT`; - if (column.un) - str += `.UNSIGNED`; + if (column.un) str += `.UNSIGNED`; break; - case "float": + case 'float': str += `DataTypes.FLOAT`; break; - case "decimal": + case 'decimal': str += `DataTypes.DECIMAL`; break; - case "double": + case 'double': str += `"DOUBLE(${column.dtxp},${column.ns})"`; break; - case "real": + case 'real': str += `DataTypes.FLOAT`; break; - case "bit": + case 'bit': str += `DataTypes.BOOLEAN`; break; - case "boolean": + case 'boolean': str += `DataTypes.STRING(45)`; break; - case "serial": + case 'serial': str += `DataTypes.BIGINT`; break; - case "date": + case 'date': str += `DataTypes.DATEONLY`; break; - case "datetime": + case 'datetime': str += `DataTypes.DATE`; break; - case "timestamp": + case 'timestamp': str += `DataTypes.DATE`; break; - case "time": + case 'time': str += `DataTypes.TIME`; break; - case "year": + case 'year': str += `"YEAR"`; break; - case "char": + case 'char': str += `DataTypes.CHAR(${column.dtxp})`; break; - case "varchar": + case 'varchar': str += `DataTypes.STRING(${column.dtxp})`; break; - case "nchar": + case 'nchar': str += `DataTypes.CHAR(${column.dtxp})`; break; - case "text": + case 'text': str += `DataTypes.TEXT`; break; - case "tinytext": + case 'tinytext': str += `DataTypes.TEXT`; break; - case "mediumtext": + case 'mediumtext': str += `DataTypes.TEXT`; break; - case "longtext": + case 'longtext': str += `DataTypes.TEXT`; break; - case "binary": + case 'binary': str += `"BINARY(${column.dtxp})"`; break; - case "varbinary": + case 'varbinary': str += `"VARBINARY(${column.dtxp})"`; break; - case "blob": + case 'blob': str += `"BLOB"`; break; - case "tinyblob": + case 'tinyblob': str += `"TINYBLOB"`; break; - case "mediumblob": + case 'mediumblob': str += `"MEDIUMBLOB"`; break; - case "longblob": + case 'longblob': str += `"LONGBLOB"`; break; - case "enum": + case 'enum': str += `DataTypes.ENUM(${column.dtxp})`; break; - case "set": + case 'set': str += `"SET(${column.dtxp})"`; break; - case "geometry": + case 'geometry': str += `DataTypes.GEOMETRY`; break; - case "point": + case 'point': str += `"POINT"`; break; - case "linestring": + case 'linestring': str += `"LINESTRING"`; break; - case "polygon": + case 'polygon': str += `"POLYGON"`; break; - case "multipoint": + case 'multipoint': str += `"MULTIPOINT"`; break; - case "multilinestring": + case 'multilinestring': str += `"MULTILINESTRING"`; break; - case "multipolygon": + case 'multipolygon': str += `"MULTIPOLYGON"`; break; - case "json": + case 'json': str += `DataTypes.JSON`; break; default: @@ -804,133 +778,131 @@ class ModelXcMetaPg extends BaseModelXcMeta { _sequelizeGetDefault(column) { let str = ''; switch (column.dt) { - case "int": + case 'int': str += `'${column.cdf}'`; break; - case "tinyint": + case 'tinyint': str += `'${column.cdf}'`; break; - case "smallint": + case 'smallint': str += `'${column.cdf}'`; break; - case "mediumint": + case 'mediumint': str += `'${column.cdf}'`; break; - case "bigint": + case 'bigint': str += `'${column.cdf}'`; break; - case "float": + case 'float': str += `'${column.cdf}'`; break; - case "decimal": + case 'decimal': str += `'${column.cdf}'`; break; - case "double": + case 'double': str += `'${column.cdf}'`; break; - case "real": + case 'real': str += `'${column.cdf}'`; break; - case "bit": + case 'bit': str += column.cdf ? column.cdf.split('b')[1] : column.cdf; break; - case "boolean": + case 'boolean': str += column.cdf; break; - case "serial": + case 'serial': str += column.cdf; break; - case "date": + case 'date': str += `sequelize.literal('${column.cdf_sequelize}')`; break; - case "datetime": + case 'datetime': str += `sequelize.literal('${column.cdf_sequelize}')`; break; - case "timestamp": + case 'timestamp': str += `sequelize.literal('${column.cdf_sequelize}')`; break; - case "time": + case 'time': str += `'${column.cdf}'`; break; - case "year": + case 'year': str += `'${column.cdf}'`; break; - case "char": + case 'char': str += `'${column.cdf}'`; break; - case "varchar": + case 'varchar': str += `'${column.cdf}'`; break; - case "nchar": + case 'nchar': str += `'${column.cdf}'`; break; - case "text": + case 'text': str += column.cdf; break; - case "tinytext": + case 'tinytext': str += column.cdf; break; - case "mediumtext": + case 'mediumtext': str += column.cdf; break; - case "longtext": + case 'longtext': str += column.cdf; break; - case "binary": + case 'binary': str += column.cdf; break; - case "varbinary": + case 'varbinary': str += column.cdf; break; - case "blob": + case 'blob': str += column.cdf; break; - case "tinyblob": + case 'tinyblob': str += column.cdf; break; - case "mediumblob": + case 'mediumblob': str += column.cdf; break; - case "longblob": + case 'longblob': str += column.cdf; break; - case "enum": + case 'enum': str += `'${column.cdf}'`; break; - case "set": + case 'set': str += `'${column.cdf}'`; break; - case "geometry": + case 'geometry': str += `'${column.cdf}'`; break; - case "point": + case 'point': str += `'${column.cdf}'`; break; - case "linestring": + case 'linestring': str += `'${column.cdf}'`; break; - case "polygon": + case 'polygon': str += `'${column.cdf}'`; break; - case "multipoint": + case 'multipoint': str += `'${column.cdf}'`; break; - case "multilinestring": + case 'multilinestring': str += `'${column.cdf}'`; break; - case "multipolygon": + case 'multipolygon': str += `'${column.cdf}'`; break; - case "json": + case 'json': str += column.cdf; break; } return str; } - getXcColumnsObject(args) { - const columnsArr = []; for (const column of args.columns) { @@ -947,7 +919,7 @@ class ModelXcMetaPg extends BaseModelXcMeta { uidt: column.uidt || this._getUIDataType(column), uip: column.uip, uicn: column.uicn, - ...column, + ...column }; if (column.rqd) { @@ -979,14 +951,14 @@ class ModelXcMetaPg extends BaseModelXcMeta { columnObj.dtxs = column.dtxs; } - columnsArr.push(columnObj) + columnsArr.push(columnObj); } this.mapDefaultPrimaryValue(columnsArr); return columnsArr; } -/* getObject() { + /* getObject() { return { tn: this.ctx.tn, _tn: this.ctx._tn, @@ -999,8 +971,6 @@ class ModelXcMetaPg extends BaseModelXcMeta { } }*/ - } - export default ModelXcMetaPg; diff --git a/packages/nocodb/src/lib/sqlMgr/code/models/xc/ModelXcMetaSqlite.ts b/packages/nocodb/src/lib/sqlMgr/code/models/xc/ModelXcMetaSqlite.ts index ab8d920286..6016c45cde 100644 --- a/packages/nocodb/src/lib/sqlMgr/code/models/xc/ModelXcMetaSqlite.ts +++ b/packages/nocodb/src/lib/sqlMgr/code/models/xc/ModelXcMetaSqlite.ts @@ -1,7 +1,6 @@ -import BaseModelXcMeta from "./BaseModelXcMeta"; +import BaseModelXcMeta from './BaseModelXcMeta'; class ModelXcMetaSqlite extends BaseModelXcMeta { - /** * @param dir * @param filename @@ -10,15 +9,14 @@ class ModelXcMetaSqlite extends BaseModelXcMeta { * @param ctx.columns * @param ctx.relations */ - constructor({dir, filename, ctx}) { - super({dir, filename, ctx}); + constructor({ dir, filename, ctx }) { + super({ dir, filename, ctx }); } /** * Prepare variables used in code template */ prepare() { - const data: any = {}; /* example of simple variable */ @@ -56,10 +54,8 @@ class ModelXcMetaSqlite extends BaseModelXcMeta { }; return data; - } - _renderXcHasMany(args) { return JSON.stringify(args.hasMany); } @@ -68,7 +64,6 @@ class ModelXcMetaSqlite extends BaseModelXcMeta { return JSON.stringify(args.belongsTo); } - /** * * @param args @@ -78,38 +73,29 @@ class ModelXcMetaSqlite extends BaseModelXcMeta { * @private */ _renderXcColumns(args) { - let str = '[\r\n'; for (let i = 0; i < args.columns.length; ++i) { - str += `{\r\n`; str += `cn: '${args.columns[i].cn}',\r\n`; str += `type: '${this._getAbstractType(args.columns[i])}',\r\n`; str += `dt: '${args.columns[i].dt}',\r\n`; - if (args.columns[i].rqd) - str += `rqd: ${args.columns[i].rqd},\r\n`; + if (args.columns[i].rqd) str += `rqd: ${args.columns[i].rqd},\r\n`; if (args.columns[i].cdf) { str += `default: "${args.columns[i].cdf}",\r\n`; str += `columnDefault: "${args.columns[i].cdf}",\r\n`; } - if (args.columns[i].un) - str += `un: ${args.columns[i].un},\r\n`; + if (args.columns[i].un) str += `un: ${args.columns[i].un},\r\n`; - if (args.columns[i].pk) - str += `pk: ${args.columns[i].pk},\r\n`; + if (args.columns[i].pk) str += `pk: ${args.columns[i].pk},\r\n`; - if (args.columns[i].ai) - str += `ai: ${args.columns[i].ai},\r\n`; + if (args.columns[i].ai) str += `ai: ${args.columns[i].ai},\r\n`; - if (args.columns[i].dtxp) - str += `dtxp: "${args.columns[i].dtxp}",\r\n`; - - if (args.columns[i].dtxs) - str += `dtxs: ${args.columns[i].dtxs},\r\n`; + if (args.columns[i].dtxp) str += `dtxp: "${args.columns[i].dtxp}",\r\n`; + if (args.columns[i].dtxs) str += `dtxs: ${args.columns[i].dtxs},\r\n`; str += `validate: { func: [], @@ -117,83 +103,80 @@ class ModelXcMetaSqlite extends BaseModelXcMeta { msg: [] },`; str += `},\r\n`; - } str += ']\r\n'; return str; - } - _getAbstractType(column) { let str = ''; switch (column.dt) { - case 'int': + case 'int': str = 'integer'; break; - case 'integer': + case 'integer': str = 'integer'; break; - case 'tinyint': + case 'tinyint': str = 'integer'; break; - case 'smallint': + case 'smallint': str = 'integer'; break; - case 'mediumint': + case 'mediumint': str = 'integer'; break; - case 'bigint': + case 'bigint': str = 'bigInteger'; break; - case 'int2': + case 'int2': str = 'integer'; break; - case 'int8': + case 'int8': str = 'integer'; break; - case 'character': + case 'character': str = 'string'; break; - case 'blob sub_type text': + case 'blob sub_type text': str = 'text'; break; - case 'numeric': + case 'numeric': str = 'float'; break; - case 'blob': + case 'blob': str = 'blob'; break; - case 'real': + case 'real': str = 'float'; break; - case 'double': + case 'double': str = 'decimal'; break; - case 'double precision': + case 'double precision': str = 'decimal'; break; - case 'float': + case 'float': str = 'float'; break; - case 'boolean': + case 'boolean': str = 'boolean'; break; - case 'date': + case 'date': str = 'date'; break; - case 'datetime': + case 'datetime': str = 'datetime'; break; - case 'text': + case 'text': str = 'text'; break; - case 'varchar': + case 'varchar': str = 'string'; break; - case 'timestamp': + case 'timestamp': str = 'timestamp'; break; default: @@ -206,138 +189,133 @@ class ModelXcMetaSqlite extends BaseModelXcMeta { _sequelizeGetType(column) { let str = ''; switch (column.dt) { - case "int": + case 'int': str += `DataTypes.INTEGER(${column.dtxp})`; - if (column.un) - str += `.UNSIGNED`; + if (column.un) str += `.UNSIGNED`; break; - case "tinyint": + case 'tinyint': str += `DataTypes.INTEGER(${column.dtxp})`; - if (column.un) - str += `.UNSIGNED`; + if (column.un) str += `.UNSIGNED`; break; - case "smallint": + case 'smallint': str += `DataTypes.INTEGER(${column.dtxp})`; - if (column.un) - str += `.UNSIGNED`; + if (column.un) str += `.UNSIGNED`; break; - case "mediumint": + case 'mediumint': str += `DataTypes.INTEGER(${column.dtxp})`; - if (column.un) - str += `.UNSIGNED`; + if (column.un) str += `.UNSIGNED`; break; - case "bigint": + case 'bigint': str += `DataTypes.BIGINT`; - if (column.un) - str += `.UNSIGNED`; + if (column.un) str += `.UNSIGNED`; break; - case "float": + case 'float': str += `DataTypes.FLOAT`; break; - case "decimal": + case 'decimal': str += `DataTypes.DECIMAL`; break; - case "double": + case 'double': str += `"DOUBLE(${column.dtxp},${column.ns})"`; break; - case "real": + case 'real': str += `DataTypes.FLOAT`; break; - case "bit": + case 'bit': str += `DataTypes.BOOLEAN`; break; - case "boolean": + case 'boolean': str += `DataTypes.STRING(45)`; break; - case "serial": + case 'serial': str += `DataTypes.BIGINT`; break; - case "date": + case 'date': str += `DataTypes.DATEONLY`; break; - case "datetime": + case 'datetime': str += `DataTypes.DATE`; break; - case "timestamp": + case 'timestamp': str += `DataTypes.DATE`; break; - case "time": + case 'time': str += `DataTypes.TIME`; break; - case "year": + case 'year': str += `"YEAR"`; break; - case "char": + case 'char': str += `DataTypes.CHAR(${column.dtxp})`; break; - case "varchar": + case 'varchar': str += `DataTypes.STRING(${column.dtxp})`; break; - case "nchar": + case 'nchar': str += `DataTypes.CHAR(${column.dtxp})`; break; - case "text": + case 'text': str += `DataTypes.TEXT`; break; - case "tinytext": + case 'tinytext': str += `DataTypes.TEXT`; break; - case "mediumtext": + case 'mediumtext': str += `DataTypes.TEXT`; break; - case "longtext": + case 'longtext': str += `DataTypes.TEXT`; break; - case "binary": + case 'binary': str += `"BINARY(${column.dtxp})"`; break; - case "varbinary": + case 'varbinary': str += `"VARBINARY(${column.dtxp})"`; break; - case "blob": + case 'blob': str += `"BLOB"`; break; - case "tinyblob": + case 'tinyblob': str += `"TINYBLOB"`; break; - case "mediumblob": + case 'mediumblob': str += `"MEDIUMBLOB"`; break; - case "longblob": + case 'longblob': str += `"LONGBLOB"`; break; - case "enum": + case 'enum': str += `DataTypes.ENUM(${column.dtxp})`; break; - case "set": + case 'set': str += `"SET(${column.dtxp})"`; break; - case "geometry": + case 'geometry': str += `DataTypes.GEOMETRY`; break; - case "point": + case 'point': str += `"POINT"`; break; - case "linestring": + case 'linestring': str += `"LINESTRING"`; break; - case "polygon": + case 'polygon': str += `"POLYGON"`; break; - case "multipoint": + case 'multipoint': str += `"MULTIPOINT"`; break; - case "multilinestring": + case 'multilinestring': str += `"MULTILINESTRING"`; break; - case "multipolygon": + case 'multipolygon': str += `"MULTIPOLYGON"`; break; - case "json": + case 'json': str += `DataTypes.JSON`; break; default: @@ -350,133 +328,131 @@ class ModelXcMetaSqlite extends BaseModelXcMeta { _sequelizeGetDefault(column) { let str = ''; switch (column.dt) { - case "int": + case 'int': str += `'${column.cdf}'`; break; - case "tinyint": + case 'tinyint': str += `'${column.cdf}'`; break; - case "smallint": + case 'smallint': str += `'${column.cdf}'`; break; - case "mediumint": + case 'mediumint': str += `'${column.cdf}'`; break; - case "bigint": + case 'bigint': str += `'${column.cdf}'`; break; - case "float": + case 'float': str += `'${column.cdf}'`; break; - case "decimal": + case 'decimal': str += `'${column.cdf}'`; break; - case "double": + case 'double': str += `'${column.cdf}'`; break; - case "real": + case 'real': str += `'${column.cdf}'`; break; - case "bit": + case 'bit': str += column.cdf ? column.cdf.split('b')[1] : column.cdf; break; - case "boolean": + case 'boolean': str += column.cdf; break; - case "serial": + case 'serial': str += column.cdf; break; - case "date": + case 'date': str += `sequelize.literal('${column.cdf_sequelize}')`; break; - case "datetime": + case 'datetime': str += `sequelize.literal('${column.cdf_sequelize}')`; break; - case "timestamp": + case 'timestamp': str += `sequelize.literal('${column.cdf_sequelize}')`; break; - case "time": + case 'time': str += `'${column.cdf}'`; break; - case "year": + case 'year': str += `'${column.cdf}'`; break; - case "char": + case 'char': str += `'${column.cdf}'`; break; - case "varchar": + case 'varchar': str += `'${column.cdf}'`; break; - case "nchar": + case 'nchar': str += `'${column.cdf}'`; break; - case "text": + case 'text': str += column.cdf; break; - case "tinytext": + case 'tinytext': str += column.cdf; break; - case "mediumtext": + case 'mediumtext': str += column.cdf; break; - case "longtext": + case 'longtext': str += column.cdf; break; - case "binary": + case 'binary': str += column.cdf; break; - case "varbinary": + case 'varbinary': str += column.cdf; break; - case "blob": + case 'blob': str += column.cdf; break; - case "tinyblob": + case 'tinyblob': str += column.cdf; break; - case "mediumblob": + case 'mediumblob': str += column.cdf; break; - case "longblob": + case 'longblob': str += column.cdf; break; - case "enum": + case 'enum': str += `'${column.cdf}'`; break; - case "set": + case 'set': str += `'${column.cdf}'`; break; - case "geometry": + case 'geometry': str += `'${column.cdf}'`; break; - case "point": + case 'point': str += `'${column.cdf}'`; break; - case "linestring": + case 'linestring': str += `'${column.cdf}'`; break; - case "polygon": + case 'polygon': str += `'${column.cdf}'`; break; - case "multipoint": + case 'multipoint': str += `'${column.cdf}'`; break; - case "multilinestring": + case 'multilinestring': str += `'${column.cdf}'`; break; - case "multipolygon": + case 'multipolygon': str += `'${column.cdf}'`; break; - case "json": + case 'json': str += column.cdf; break; } return str; } - public getXcColumnsObject(args): any { - const columnsArr = []; for (const column of args.columns) { @@ -493,7 +469,7 @@ class ModelXcMetaSqlite extends BaseModelXcMeta { uidt: column.uidt || this._getUIDataType(column), uip: column.uip, uicn: column.uicn, - ...column, + ...column }; if (column.rqd) { @@ -525,13 +501,13 @@ class ModelXcMetaSqlite extends BaseModelXcMeta { columnObj.dtxs = column.dtxs; } - columnsArr.push(columnObj) + columnsArr.push(columnObj); } this.mapDefaultPrimaryValue(columnsArr); return columnsArr; } -/* public getObject(): any { + /* public getObject(): any { return { tn: this.ctx.tn, _tn: this.ctx._tn, @@ -549,57 +525,56 @@ class ModelXcMetaSqlite extends BaseModelXcMeta { switch (this.getAbstractType(col)) { case 'integer': return 'Number'; - case "boolean": + case 'boolean': return 'Checkbox'; - case 'float': + case 'float': return 'Decimal'; - case "date": + case 'date': return 'Date'; - case "datetime": + case 'datetime': return 'DateTime'; - case "time": + case 'time': return 'Time'; case 'year': return 'Year'; - case 'string': + case 'string': return 'SingleLineText'; - case "text": + case 'text': return 'LongText'; - case "enum": + case 'enum': return 'SingleSelect'; - case "set": + case 'set': return 'MultiSelect'; - case "json": + case 'json': return 'LongText'; case 'blob': - return 'LongText' + return 'LongText'; case 'geometry': - return 'Geometry' + return 'Geometry'; default: - return 'SpecificDBType' + return 'SpecificDBType'; } - } private getAbstractType(col): any { switch ((col.dt || col.dt).toLowerCase()) { case 'date': return 'date'; - case 'datetime': + case 'datetime': case 'timestamp': return 'datetime'; - case "integer": - case 'int': - case 'tinyint': - case 'smallint': - case 'mediumint': - case 'bigint': - case 'int2': - case 'int8': - return 'integer' - case "text": - return 'text' - case 'boolean': + case 'integer': + case 'int': + case 'tinyint': + case 'smallint': + case 'mediumint': + case 'bigint': + case 'int2': + case 'int8': + return 'integer'; + case 'text': + return 'text'; + case 'boolean': return 'boolean'; case 'real': case 'double': @@ -610,17 +585,13 @@ class ModelXcMetaSqlite extends BaseModelXcMeta { case 'blob sub_type text': case 'blob': - return 'blob' + return 'blob'; case 'character': case 'varchar': - return 'string' - + return 'string'; } } - - } - export default ModelXcMetaSqlite; diff --git a/packages/nocodb/src/lib/sqlMgr/code/policies/xc/ExpressXcPolicy.ts b/packages/nocodb/src/lib/sqlMgr/code/policies/xc/ExpressXcPolicy.ts index 6fbb43f600..32e7f959fa 100644 --- a/packages/nocodb/src/lib/sqlMgr/code/policies/xc/ExpressXcPolicy.ts +++ b/packages/nocodb/src/lib/sqlMgr/code/policies/xc/ExpressXcPolicy.ts @@ -1,12 +1,9 @@ -import lodash from "lodash"; - -import {Acl} from "../../../../../interface/config"; -import BaseRender from "../../BaseRender"; +import lodash from 'lodash'; +import { Acl } from '../../../../../interface/config'; +import BaseRender from '../../BaseRender'; class ExpressXcMiddleware extends BaseRender { - - /** * * @param dir @@ -16,17 +13,15 @@ class ExpressXcMiddleware extends BaseRender { * @param ctx.columns * @param ctx.relations */ - constructor({dir, filename, ctx}) { - super({dir, filename, ctx}); + constructor({ dir, filename, ctx }) { + super({ dir, filename, ctx }); } /** * Prepare variables used in code template */ prepare() { - - - let data:any = {}; + let data: any = {}; /* example of simple variable */ data = this.ctx; @@ -57,16 +52,15 @@ class ExpressXcMiddleware extends BaseRender { }; return data; - } - private _renderXcHasManyRoutePermissions(args) { - - + private _renderXcHasManyRoutePermissions(args) { let str = ''; - let hmRelations = args.relations ? args.relations.filter(r => r.rtn === args.tn) : []; + let hmRelations = args.relations + ? args.relations.filter(r => r.rtn === args.tn) + : []; if (hmRelations.length > 1) - hmRelations = lodash.uniqBy(hmRelations, function (e) { + hmRelations = lodash.uniqBy(hmRelations, function(e) { return [e.tn, e.rtn].join(); }); for (let i = 0; i < hmRelations.length; ++i) { @@ -82,72 +76,79 @@ class ExpressXcMiddleware extends BaseRender { return str; - /* iterate over has many relations */ } _renderXcBelongsToRoutePermissions(args) { - let str = ''; // - let btRelations = args.relations ? args.relations.filter(r => r.tn === args.tn) : []; + let btRelations = args.relations + ? args.relations.filter(r => r.tn === args.tn) + : []; if (btRelations.length > 1) - btRelations = lodash.uniqBy(btRelations, function (e) { + btRelations = lodash.uniqBy(btRelations, function(e) { return [e.tn, e.rtn].join(); }); for (let i = 0; i < btRelations.length; ++i) { - str += `'/api/${args.routeVersionLetter}1/${args.tn}/belongs/:parents' : {get:{admin:true,user:true,guest:true}},` + str += `'/api/${args.routeVersionLetter}1/${args.tn}/belongs/:parents' : {get:{admin:true,user:true,guest:true}},`; } return str; } - getObject():Acl { + getObject(): Acl { return { creator: { read: true, - ...(this.ctx.type !== 'view' ? { - create: true, - update: true, - delete: true, - } : {}), + ...(this.ctx.type !== 'view' + ? { + create: true, + update: true, + delete: true + } + : {}) }, editor: { read: true, - ...(this.ctx.type !== 'view' ? { - create: true, - update: true, - delete: true, - } : {}), + ...(this.ctx.type !== 'view' + ? { + create: true, + update: true, + delete: true + } + : {}) }, commenter: { read: true, - ...(this.ctx.type !== 'view' ? { - create: false, - update: false, - delete: false, - } : {}), + ...(this.ctx.type !== 'view' + ? { + create: false, + update: false, + delete: false + } + : {}) }, viewer: { read: true, - ...(this.ctx.type !== 'view' ? { - create: false, - update: false, - delete: false, - } : {}), + ...(this.ctx.type !== 'view' + ? { + create: false, + update: false, + delete: false + } + : {}) }, guest: { read: false, - ...(this.ctx.type !== 'view' ? { - create: false, - update: false, - delete: false, - } : {}), - }, - } + ...(this.ctx.type !== 'view' + ? { + create: false, + update: false, + delete: false + } + : {}) + } + }; } - - } - export default ExpressXcMiddleware; diff --git a/packages/nocodb/src/lib/sqlMgr/code/routers/xc-ts/SwaggerTypes.ts b/packages/nocodb/src/lib/sqlMgr/code/routers/xc-ts/SwaggerTypes.ts index 4c1f7a2f3a..5fbbbe14a2 100644 --- a/packages/nocodb/src/lib/sqlMgr/code/routers/xc-ts/SwaggerTypes.ts +++ b/packages/nocodb/src/lib/sqlMgr/code/routers/xc-ts/SwaggerTypes.ts @@ -1,214 +1,211 @@ class SwaggerTypes { - static setSwaggerType(column, field, dbType = 'mysql') { switch (dbType) { - case "mysql": - case "mariadb": - SwaggerTypes.setSwaggerTypeForMysql(column, field) + case 'mysql': + case 'mariadb': + SwaggerTypes.setSwaggerTypeForMysql(column, field); break; - case "pg": - SwaggerTypes.setSwaggerTypeForPg(column, field) + case 'pg': + SwaggerTypes.setSwaggerTypeForPg(column, field); break; - case "mssql": - SwaggerTypes.setSwaggerTypeForMssql(column, field) + case 'mssql': + SwaggerTypes.setSwaggerTypeForMssql(column, field); break; - case "sqlite3": - SwaggerTypes.setSwaggerTypeForSqlite(column, field) + case 'sqlite3': + SwaggerTypes.setSwaggerTypeForSqlite(column, field); break; } } static setSwaggerTypeForMysql(column, field) { switch (column.dt) { - case "int": - case "tinyint": - case "smallint": - case "mediumint": - case "bigint": - field.type = 'integer' - break; - case "float": - case "decimal": - case "real": - field.type = 'number' - break; - case "double": - field.type = 'number' - field.format = 'double' - break; - case "bit": - field.type = 'integer' - break; - case "boolean": - field.type = 'boolean' - break; - case "serial": - field.type = 'string' - break; - case "date": - case "datetime": - case "timestamp": - case "time": - case "year": - case "char": - case "varchar": - case "nchar": - case "text": - case "tinytext": - case "mediumtext": - case "longtext": - case "varbinary": - case "blob": - case "tinyblob": - case "mediumblob": - case "longblob": - case "enum": - case "set": - case "geometry": - case "point": - case "linestring": - case "polygon": - case "multipoint": - case "multilinestring": - case "multipolygon": + case 'int': + case 'tinyint': + case 'smallint': + case 'mediumint': + case 'bigint': + field.type = 'integer'; + break; + case 'float': + case 'decimal': + case 'real': + field.type = 'number'; + break; + case 'double': + field.type = 'number'; + field.format = 'double'; + break; + case 'bit': + field.type = 'integer'; + break; + case 'boolean': + field.type = 'boolean'; + break; + case 'serial': field.type = 'string'; break; - case "binary": + case 'date': + case 'datetime': + case 'timestamp': + case 'time': + case 'year': + case 'char': + case 'varchar': + case 'nchar': + case 'text': + case 'tinytext': + case 'mediumtext': + case 'longtext': + case 'varbinary': + case 'blob': + case 'tinyblob': + case 'mediumblob': + case 'longblob': + case 'enum': + case 'set': + case 'geometry': + case 'point': + case 'linestring': + case 'polygon': + case 'multipoint': + case 'multilinestring': + case 'multipolygon': + field.type = 'string'; + break; + case 'binary': field.type = 'string'; field.format = 'binary'; - break - case "json": - field.type = 'object' + break; + case 'json': + field.type = 'object'; break; default: - field.type = 'string' + field.type = 'string'; break; } } static setSwaggerTypeForPg(column, field) { switch (column.dt) { - case "int": - case "integer": - case "bigint": - case "bigserial": - case "int2": - case "int4": - case "int8": - case "int4range": - case "int8range": - case "serial": - case "serial2": - case "serial8": - case "smallint": - case "smallserial": + case 'int': + case 'integer': + case 'bigint': + case 'bigserial': + case 'int2': + case 'int4': + case 'int8': + case 'int4range': + case 'int8range': + case 'serial': + case 'serial2': + case 'serial8': + case 'smallint': + case 'smallserial': field.type = 'integer'; break; - case "char": - case "character": - case "bit": - case "date": - case "character varying": - case "text": - case "time": - case "time without time zone": - case "timestamp": - case "timestamp without time zone": - case "timestamptz": - case "timestamp with time zone": - case "timetz": - case "time with time zone": - case "daterange": - + case 'char': + case 'character': + case 'bit': + case 'date': + case 'character varying': + case 'text': + case 'time': + case 'time without time zone': + case 'timestamp': + case 'timestamp without time zone': + case 'timestamptz': + case 'timestamp with time zone': + case 'timetz': + case 'time with time zone': + case 'daterange': field.type = 'string'; break; - case "bool": - case "boolean": + case 'bool': + case 'boolean': field.type = 'boolean'; break; - case "double precision": + case 'double precision': field.type = 'number'; field.format = 'double'; break; - case "event_trigger": - case "fdw_handler": - case "float4": - case "float8": - case "real": - case "numeric": + case 'event_trigger': + case 'fdw_handler': + case 'float4': + case 'float8': + case 'real': + case 'numeric': field.type = 'number'; field.format = 'float'; break; - case "uuid": + case 'uuid': field.type = 'string'; field.format = 'uuid'; break; - - case "json": - case "jsonb": + case 'json': + case 'jsonb': field.type = 'object'; break; - case "gtsvector": - case "index_am_handler": - case "anyenum": - case "anynonarray": - case "anyrange": - case "box": - case "bpchar": - case "bytea": - case "cid": - case "cidr": - case "circle": - case "cstring": - case "inet": - case "internal": - case "interval": - case "language_handler": - case "line": - case "lsec": - case "macaddr": - case "money": - case "name": - case "numrange": - case "oid": - case "opaque": - case "path": - case "pg_ddl_command": - case "pg_lsn": - case "pg_node_tree": - case "point": - case "polygon": - case "record": - case "refcursor": - case "regclass": - case "regconfig": - case "regdictionary": - case "regnamespace": - case "regoper": - case "regoperator": - case "regproc": - case "regpreocedure": - case "regrole": - case "regtype": - case "reltime": - case "smgr": - case "tid": - case "tinterval": - case "trigger": - case "tsm_handler": - case "tsquery": - case "tsrange": - case "tstzrange": - case "tsvector": - case "txid_snapshot": - case "unknown": - case "void": - case "xid": - case "xml": + case 'gtsvector': + case 'index_am_handler': + case 'anyenum': + case 'anynonarray': + case 'anyrange': + case 'box': + case 'bpchar': + case 'bytea': + case 'cid': + case 'cidr': + case 'circle': + case 'cstring': + case 'inet': + case 'internal': + case 'interval': + case 'language_handler': + case 'line': + case 'lsec': + case 'macaddr': + case 'money': + case 'name': + case 'numrange': + case 'oid': + case 'opaque': + case 'path': + case 'pg_ddl_command': + case 'pg_lsn': + case 'pg_node_tree': + case 'point': + case 'polygon': + case 'record': + case 'refcursor': + case 'regclass': + case 'regconfig': + case 'regdictionary': + case 'regnamespace': + case 'regoper': + case 'regoperator': + case 'regproc': + case 'regpreocedure': + case 'regrole': + case 'regtype': + case 'reltime': + case 'smgr': + case 'tid': + case 'tinterval': + case 'trigger': + case 'tsm_handler': + case 'tsquery': + case 'tsrange': + case 'tstzrange': + case 'tsvector': + case 'txid_snapshot': + case 'unknown': + case 'void': + case 'xid': + case 'xml': field.type = 'string'; break; default: @@ -218,8 +215,6 @@ class SwaggerTypes { } static setSwaggerTypeForMssql(column, field) { - - switch (column.dt) { case 'bigint': case 'int': @@ -279,37 +274,37 @@ class SwaggerTypes { static setSwaggerTypeForSqlite(column, field) { switch (column.dt) { - case 'int': - case 'integer': - case 'tinyint': - case 'smallint': - case 'mediumint': - case 'bigint': - case 'int2': - case 'int8': + case 'int': + case 'integer': + case 'tinyint': + case 'smallint': + case 'mediumint': + case 'bigint': + case 'int2': + case 'int8': field.type = 'integer'; break; - case 'character': - case 'numeric': - case 'real': - case 'float': + case 'character': + case 'numeric': + case 'real': + case 'float': field.type = 'number'; break; - case 'double': - case 'double precision': + case 'double': + case 'double precision': field.type = 'number'; field.format = 'double'; break; - case 'boolean': + case 'boolean': field.type = 'boolean'; break; - case 'date': - case 'datetime': - case 'text': - case 'blob': - case 'blob sub_type text': - case 'varchar': - case 'timestamp': + case 'date': + case 'datetime': + case 'text': + case 'blob': + case 'blob sub_type text': + case 'varchar': + case 'timestamp': field.type = 'string'; break; default: @@ -317,9 +312,6 @@ class SwaggerTypes { break; } } - - } - export default SwaggerTypes; diff --git a/packages/nocodb/src/lib/sqlMgr/code/routers/xc-ts/SwaggerXc.ts b/packages/nocodb/src/lib/sqlMgr/code/routers/xc-ts/SwaggerXc.ts index bc70c40554..5ff442b7f4 100644 --- a/packages/nocodb/src/lib/sqlMgr/code/routers/xc-ts/SwaggerXc.ts +++ b/packages/nocodb/src/lib/sqlMgr/code/routers/xc-ts/SwaggerXc.ts @@ -1,9 +1,8 @@ -import BaseRender from "../../BaseRender"; +import BaseRender from '../../BaseRender'; -import SwaggerTypes from "./SwaggerTypes"; +import SwaggerTypes from './SwaggerTypes'; class SwaggerXc extends BaseRender { - /** * * @param dir @@ -13,16 +12,14 @@ class SwaggerXc extends BaseRender { * @param ctx.columns * @param ctx.relations */ - constructor({dir, filename, ctx}: any) { - super({dir, filename, ctx}); + constructor({ dir, filename, ctx }: any) { + super({ dir, filename, ctx }); } /** * Prepare variables used in code template */ prepare() { - - let data: any = {}; /* example of simple variable */ @@ -40,7 +37,6 @@ class SwaggerXc extends BaseRender { }; return data; - } /** @@ -55,7 +51,6 @@ class SwaggerXc extends BaseRender { const obj = this.getDefenitions(args); return JSON.stringify(obj); - } getDefenitions(args) { @@ -75,7 +70,7 @@ class SwaggerXc extends BaseRender { for (const column of args.columns) { const field: any = {}; - SwaggerTypes.setSwaggerType(column, field, args.dbType) + SwaggerTypes.setSwaggerType(column, field, args.dbType); if (column.rqd) { field.nullable = false; @@ -88,8 +83,8 @@ class SwaggerXc extends BaseRender { properties[column._cn] = field; } - properties = Object.assign(obj[`${args._tn}Nested`].properties, properties) - for (const column of (args.v || [])) { + properties = Object.assign(obj[`${args._tn}Nested`].properties, properties); + for (const column of args.v || []) { const field: any = {}; field.readOnly = true; let _cn = column._cn; @@ -105,614 +100,543 @@ class SwaggerXc extends BaseRender { field.items = { $ref: `#/definitions/${column.hm?._tn}` }; - field.$ref = `#/definitions/${column.hm?._tn}` + field.$ref = `#/definitions/${column.hm?._tn}`; _cn = `${column.hm?._tn}List`; } else if (column.bt) { - field.$ref = `#/definitions/${column.bt?._rtn}` + field.$ref = `#/definitions/${column.bt?._rtn}`; _cn = `${column.bt?._rtn}Read`; } properties[_cn] = field; - } return obj; } - getObject() { - return { - "tags": [ + tags: [ { - "name": `${this.ctx._tn}`, - "description": `Everything about your ${this.ctx._tn}` + name: `${this.ctx._tn}`, + description: `Everything about your ${this.ctx._tn}` } ], - "paths": { + paths: { [`/nc/${this.ctx.project_id}/api/${this.ctx.routeVersionLetter}/${this.ctx._tn}`]: { - "post": { - "tags": [ - `${this.ctx._tn}` - ], - "summary": `Add a new ${this.ctx._tn}`, - "description": "", - "operationId": `add${this.ctx._tn}`, - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "parameters": [ - { - "in": "body", - "name": "body", - "description": `${this.ctx._tn} object that needs to add`, - "required": true, - "schema": { - "$ref": `#/definitions/${this.ctx._tn}` + post: { + tags: [`${this.ctx._tn}`], + summary: `Add a new ${this.ctx._tn}`, + description: '', + operationId: `add${this.ctx._tn}`, + consumes: ['application/json'], + produces: ['application/json'], + parameters: [ + { + in: 'body', + name: 'body', + description: `${this.ctx._tn} object that needs to add`, + required: true, + schema: { + $ref: `#/definitions/${this.ctx._tn}` } } ], - "responses": { - "405": { - "description": "Invalid input" + responses: { + '405': { + description: 'Invalid input' } } }, - "get": { - "tags": [ - `${this.ctx._tn}` - ], - "summary": "Get list", - "description": "", - "operationId": `get${this.ctx._tn}`, - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "parameters": [ + get: { + tags: [`${this.ctx._tn}`], + summary: 'Get list', + description: '', + operationId: `get${this.ctx._tn}`, + consumes: ['application/json'], + produces: ['application/json'], + parameters: [ { - "in": "query", - "name": "fields", - "type": "string", - "description": "Comma separated fields from the model" + in: 'query', + name: 'fields', + type: 'string', + description: 'Comma separated fields from the model' }, { - "in": "query", - "name": "bt", - "type": "string", - "description": "Comma separated parent table names(Belongs To)" + in: 'query', + name: 'bt', + type: 'string', + description: 'Comma separated parent table names(Belongs To)' }, { - "in": "query", - "name": "hm", - "type": "string", - "description": "Comma separated child table names(Has Many)" + in: 'query', + name: 'hm', + type: 'string', + description: 'Comma separated child table names(Has Many)' }, { - "in": "query", - "name": "mm", - "type": "string", - "description": "Comma separated child table names(Many to Many)" + in: 'query', + name: 'mm', + type: 'string', + description: 'Comma separated child table names(Many to Many)' }, { - "in": "query", - "name": "where", - "type": "string", - "description": "Where expression" + in: 'query', + name: 'where', + type: 'string', + description: 'Where expression' }, { - "in": "query", - "name": "limit", - "description": "Page size limit", - "type": "integer", - "format": "int64" + in: 'query', + name: 'limit', + description: 'Page size limit', + type: 'integer', + format: 'int64' }, { - "in": "query", - "name": "offset", - "description": "Pagination offset", - "type": "integer", - "format": "int64" + in: 'query', + name: 'offset', + description: 'Pagination offset', + type: 'integer', + format: 'int64' }, { - "in": "query", - "name": "sort", - "description": - "Comma separated sort fields", - "type": "string" + in: 'query', + name: 'sort', + description: 'Comma separated sort fields', + type: 'string' } - ], - "responses": { - "405": { - "description": "Invalid input" - }, - "200": { - "description": "successful operation", - "schema": { - type: "array", + responses: { + '405': { + description: 'Invalid input' + }, + '200': { + description: 'successful operation', + schema: { + type: 'array', items: { - "$ref": `#/definitions/${this.ctx._tn}Nested` + $ref: `#/definitions/${this.ctx._tn}Nested` } } - }, + } } } }, [`/nc/${this.ctx.project_id}/api/${this.ctx.routeVersionLetter}/${this.ctx._tn}/{${this.ctx._tn}Id}`]: { - "get": { - "tags": [ - `${this.ctx._tn}` - ], - "summary": `Find ${this.ctx._tn} by ID`, - "description": `Returns a single ${this.ctx._tn}`, - "operationId": `get${this.ctx._tn}ById`, - "produces": [ - "application/json" - ], - "parameters": [ + get: { + tags: [`${this.ctx._tn}`], + summary: `Find ${this.ctx._tn} by ID`, + description: `Returns a single ${this.ctx._tn}`, + operationId: `get${this.ctx._tn}ById`, + produces: ['application/json'], + parameters: [ { - "name": `${this.ctx._tn}Id`, - "in": "path", - "description": `ID of ${this.ctx._tn} to return. In case of composite key provide keys separated by ___`, - "required": true, - "type": "string" + name: `${this.ctx._tn}Id`, + in: 'path', + description: `ID of ${this.ctx._tn} to return. In case of composite key provide keys separated by ___`, + required: true, + type: 'string' }, { - "in": "query", - "name": "bt", - "type": "string", - "description": "Comma separated parent table names(Belongs To)" + in: 'query', + name: 'bt', + type: 'string', + description: 'Comma separated parent table names(Belongs To)' }, { - "in": "query", - "name": "hm", - "type": "string", - "description": "Comma separated child table names(Has Many)" + in: 'query', + name: 'hm', + type: 'string', + description: 'Comma separated child table names(Has Many)' }, { - "in": "query", - "name": "mm", - "type": "string", - "description": "Comma separated child table names(Many to Many)" - }, + in: 'query', + name: 'mm', + type: 'string', + description: 'Comma separated child table names(Many to Many)' + } ], - "responses": { - "200": { - "description": "successful operation", - "schema": { - "$ref": `#/definitions/${this.ctx._tn}Nested` + responses: { + '200': { + description: 'successful operation', + schema: { + $ref: `#/definitions/${this.ctx._tn}Nested` } }, - "400": { - "description": "Invalid ID supplied" + '400': { + description: 'Invalid ID supplied' }, - "404": { - "description": `${this.ctx._tn} not found` + '404': { + description: `${this.ctx._tn} not found` } } }, - "put": { - "tags": [ - `${this.ctx._tn}` - ], - "summary": `Updates a ${this.ctx._tn}`, - "description": "", - "operationId": `update${this.ctx._tn}`, - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "parameters": [ - { - "name": `${this.ctx._tn}Id`, - "in": "path", - "description": `ID of ${this.ctx._tn} to return. In case of composite key provide keys separated by ___`, - "required": true, - "type": "string" - }, - { - "in": "body", - "name": "body", - "description": `${this.ctx._tn} object that needs to be added to the store`, - "required": true, - "schema": { - "$ref": `#/definitions/${this.ctx._tn}` + put: { + tags: [`${this.ctx._tn}`], + summary: `Updates a ${this.ctx._tn}`, + description: '', + operationId: `update${this.ctx._tn}`, + consumes: ['application/json'], + produces: ['application/json'], + parameters: [ + { + name: `${this.ctx._tn}Id`, + in: 'path', + description: `ID of ${this.ctx._tn} to return. In case of composite key provide keys separated by ___`, + required: true, + type: 'string' + }, + { + in: 'body', + name: 'body', + description: `${this.ctx._tn} object that needs to be added to the store`, + required: true, + schema: { + $ref: `#/definitions/${this.ctx._tn}` } } ], - "responses": { - "405": { - "description": "Invalid input" + responses: { + '405': { + description: 'Invalid input' } } }, - "delete": { - "tags": [ - `${this.ctx._tn}` - ], - "summary": `Deletes a ${this.ctx._tn}`, - "description": "", - "operationId": `delete${this.ctx._tn}`, - "produces": [ - "application/json" - ], - "parameters": [ - { - "name": `${this.ctx._tn}Id`, - "in": "path", - "description": `ID of ${this.ctx._tn} to return. In case of composite key provide keys separated by ___`, - "required": true, - "type": "string" + delete: { + tags: [`${this.ctx._tn}`], + summary: `Deletes a ${this.ctx._tn}`, + description: '', + operationId: `delete${this.ctx._tn}`, + produces: ['application/json'], + parameters: [ + { + name: `${this.ctx._tn}Id`, + in: 'path', + description: `ID of ${this.ctx._tn} to return. In case of composite key provide keys separated by ___`, + required: true, + type: 'string' } ], - "responses": { - "400": { - "description": "Invalid ID supplied" + responses: { + '400': { + description: 'Invalid ID supplied' }, - "404": { - "description": `${this.ctx._tn} not found` + '404': { + description: `${this.ctx._tn} not found` } } } }, - [`/nc/${this.ctx.project_id}/api/${this.ctx.routeVersionLetter}/${this.ctx._tn}/bulk`]: { - "post": { - "tags": [ - `${this.ctx._tn}` - ], - "summary": `Bulk ${this.ctx._tn} insert`, - "description": "", - "operationId": `bulk${this.ctx._tn}Insert`, - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "parameters": [ - { - "in": "body", - "name": "body", - "description": `${this.ctx._tn} objects`, - "required": true, - "schema": { - "type": "array", - "items": { - "$ref": `#/definitions/${this.ctx._tn}` + post: { + tags: [`${this.ctx._tn}`], + summary: `Bulk ${this.ctx._tn} insert`, + description: '', + operationId: `bulk${this.ctx._tn}Insert`, + consumes: ['application/json'], + produces: ['application/json'], + parameters: [ + { + in: 'body', + name: 'body', + description: `${this.ctx._tn} objects`, + required: true, + schema: { + type: 'array', + items: { + $ref: `#/definitions/${this.ctx._tn}` } } } ], - "responses": { - "405": { - "description": "Invalid input" + responses: { + '405': { + description: 'Invalid input' } } }, - "put": { - "tags": [ - `${this.ctx._tn}` - ], - "summary": `Updates a ${this.ctx._tn}`, - "description": "", - "operationId": `update${this.ctx._tn}`, - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "parameters": [ - { - "in": "body", - "name": "body", - "description": `${this.ctx._tn} objects with id`, - "required": true, - "schema": { - "type": "array", - "items": { - "$ref": `#/definitions/${this.ctx._tn}` + put: { + tags: [`${this.ctx._tn}`], + summary: `Updates a ${this.ctx._tn}`, + description: '', + operationId: `update${this.ctx._tn}`, + consumes: ['application/json'], + produces: ['application/json'], + parameters: [ + { + in: 'body', + name: 'body', + description: `${this.ctx._tn} objects with id`, + required: true, + schema: { + type: 'array', + items: { + $ref: `#/definitions/${this.ctx._tn}` } } } ], - "responses": { - "405": { - "description": "Invalid input" + responses: { + '405': { + description: 'Invalid input' } } }, - "delete": { - "tags": [ - `${this.ctx._tn}` - ], - "summary": `Deletes a ${this.ctx._tn}`, - "description": "", - "operationId": `delete${this.ctx._tn}`, - "produces": [ - "application/json" - ], - "parameters": [ - { - "in": "body", - "name": "body", - "description": `${this.ctx._tn} objects contains id`, - "required": true, - "schema": { - "type": "array", - "items": { - "$ref": `#/definitions/${this.ctx._tn}` + delete: { + tags: [`${this.ctx._tn}`], + summary: `Deletes a ${this.ctx._tn}`, + description: '', + operationId: `delete${this.ctx._tn}`, + produces: ['application/json'], + parameters: [ + { + in: 'body', + name: 'body', + description: `${this.ctx._tn} objects contains id`, + required: true, + schema: { + type: 'array', + items: { + $ref: `#/definitions/${this.ctx._tn}` } } } ], - "responses": { - "400": { - "description": "Invalid ID supplied" + responses: { + '400': { + description: 'Invalid ID supplied' }, - "404": { - "description": `${this.ctx._tn} not found` + '404': { + description: `${this.ctx._tn} not found` } } } }, [`/nc/${this.ctx.project_id}/api/${this.ctx.routeVersionLetter}/${this.ctx._tn}/findOne`]: { - "get": { - "tags": [ - `${this.ctx._tn}` - ], - "summary": "Get first one from filtered data", - "description": "", - "operationId": `findOne${this.ctx._tn}`, - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "parameters": [ + get: { + tags: [`${this.ctx._tn}`], + summary: 'Get first one from filtered data', + description: '', + operationId: `findOne${this.ctx._tn}`, + consumes: ['application/json'], + produces: ['application/json'], + parameters: [ { - "in": "query", - "name": "fields", - "type": "string", - "description": "Comma separated fields from the model" + in: 'query', + name: 'fields', + type: 'string', + description: 'Comma separated fields from the model' }, { - "in": "query", - "name": "where", - "type": "string", - "description": "Where expression" + in: 'query', + name: 'where', + type: 'string', + description: 'Where expression' }, { - "in": "query", - "name": "limit", - "description": "Page size limit", - "type": "integer", - "format": "int64" + in: 'query', + name: 'limit', + description: 'Page size limit', + type: 'integer', + format: 'int64' }, { - "in": "query", - "name": "offset", - "description": "Pagination offset", - "type": "integer", - "format": "int64" + in: 'query', + name: 'offset', + description: 'Pagination offset', + type: 'integer', + format: 'int64' }, { - "in": "query", - "name": "sort", - "description": "Comma separated sort fields", - "type": "string" + in: 'query', + name: 'sort', + description: 'Comma separated sort fields', + type: 'string' } ], - "responses": { - "405": { - "description": "Invalid input" - }, - "200": { - "description": "successful operation", - "schema": { - "$ref": `#/definitions/${this.ctx._tn}` + responses: { + '405': { + description: 'Invalid input' + }, + '200': { + description: 'successful operation', + schema: { + $ref: `#/definitions/${this.ctx._tn}` } } } } }, [`/nc/${this.ctx.project_id}/api/${this.ctx.routeVersionLetter}/${this.ctx._tn}/{${this.ctx._tn}Id}/exists`]: { - "get": { - "tags": [ - `${this.ctx._tn}` - ], - "summary": `Check ${this.ctx._tn} with provided ID exists`, - "description": `Returns a single ${this.ctx._tn}`, - "operationId": `check${this.ctx._tn}Exists`, - "produces": [ - "application/json" - ], - "parameters": [ - { - "name": `${this.ctx._tn}Id`, - "in": "path", - "description": `ID of ${this.ctx._tn} to return. In case of composite key provide keys separated by ___`, - "required": true, - "type": "string" + get: { + tags: [`${this.ctx._tn}`], + summary: `Check ${this.ctx._tn} with provided ID exists`, + description: `Returns a single ${this.ctx._tn}`, + operationId: `check${this.ctx._tn}Exists`, + produces: ['application/json'], + parameters: [ + { + name: `${this.ctx._tn}Id`, + in: 'path', + description: `ID of ${this.ctx._tn} to return. In case of composite key provide keys separated by ___`, + required: true, + type: 'string' } ], - "responses": { - "200": { - "description": "successful operation", - "schema": { - "type": "boolean" + responses: { + '200': { + description: 'successful operation', + schema: { + type: 'boolean' } } } } }, [`/nc/${this.ctx.project_id}/api/${this.ctx.routeVersionLetter}/${this.ctx._tn}/count`]: { - "get": { - "tags": [ - `${this.ctx._tn}` - ], - "summary": `Get ${this.ctx._tn} count`, - "operationId": `get${this.ctx._tn}Count`, - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "parameters": [ - { - "in": "query", - "name": "where", - "type": "string", - "description": "Where expression" + get: { + tags: [`${this.ctx._tn}`], + summary: `Get ${this.ctx._tn} count`, + operationId: `get${this.ctx._tn}Count`, + consumes: ['application/json'], + produces: ['application/json'], + parameters: [ + { + in: 'query', + name: 'where', + type: 'string', + description: 'Where expression' } ], - "responses": { - "405": { - "description": "Invalid input" - }, - "200": { - "description": "successful operation", - "schema": { - "type": "object" + responses: { + '405': { + description: 'Invalid input' + }, + '200': { + description: 'successful operation', + schema: { + type: 'object' } } } } }, [`/nc/${this.ctx.project_id}/api/${this.ctx.routeVersionLetter}/${this.ctx._tn}/groupby`]: { - "get": { - "tags": [ - `${this.ctx._tn}` - ], - "summary": "Group by column", - "description": "", - "operationId": `${this.ctx._tn}GroupByColumn`, - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "parameters": [ + get: { + tags: [`${this.ctx._tn}`], + summary: 'Group by column', + description: '', + operationId: `${this.ctx._tn}GroupByColumn`, + consumes: ['application/json'], + produces: ['application/json'], + parameters: [ { - "in": "query", - "name": "column_name", - "type": "string", - "description": "Column name" + in: 'query', + name: 'column_name', + type: 'string', + description: 'Column name' }, { - "in": "query", - "name": "where", - "type": "string", - "description": "Where expression" + in: 'query', + name: 'where', + type: 'string', + description: 'Where expression' }, { - "in": "query", - "name": "limit", - "description": "Page size limit", - "type": "integer", - "format": "int64" + in: 'query', + name: 'limit', + description: 'Page size limit', + type: 'integer', + format: 'int64' }, { - "in": "query", - "name": "offset", - "description": "Pagination offset", - "type": "integer", - "format": "int64" + in: 'query', + name: 'offset', + description: 'Pagination offset', + type: 'integer', + format: 'int64' }, { - "in": "query", - "name": "sort", - "description": "Comma separated sort fieldst", - "type": "string" + in: 'query', + name: 'sort', + description: 'Comma separated sort fieldst', + type: 'string' } ], - "responses": { - "405": { - "description": "Invalid input" - }, - "200": { - "description": "successful operation", - "schema": { - "$ref": `#/definitions/${this.ctx._tn}` + responses: { + '405': { + description: 'Invalid input' + }, + '200': { + description: 'successful operation', + schema: { + $ref: `#/definitions/${this.ctx._tn}` } } } } }, [`/nc/${this.ctx.project_id}/api/${this.ctx.routeVersionLetter}/${this.ctx._tn}/distribution`]: { - "get": { - "tags": [ - `${this.ctx._tn}` - ], - "summary": "", - "description": "", - "operationId": `${this.ctx._tn}Distribution`, - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "parameters": [ - { - "in": "query", - "name": "column_name", - "type": "string", - "description": "Column name" - }, { - "in": "query", - "name": "min", - "description": "min value", - "type": "integer", - "format": "int64" - }, { - "in": "query", - "name": "max", - "description": "max value", - "type": "integer", - "format": "int64" - }, { - "in": "query", - "name": "step", - "description": "step value", - "type": "integer", - "format": "int64" - }, { - "in": "query", - "name": "steps", - "description": "steps value", - "type": "integer", - "format": "int64" - }, { - "in": "query", - "name": "func", - "description": "comma separated aggregation functions", - "type": "string" + get: { + tags: [`${this.ctx._tn}`], + summary: '', + description: '', + operationId: `${this.ctx._tn}Distribution`, + consumes: ['application/json'], + produces: ['application/json'], + parameters: [ + { + in: 'query', + name: 'column_name', + type: 'string', + description: 'Column name' + }, + { + in: 'query', + name: 'min', + description: 'min value', + type: 'integer', + format: 'int64' + }, + { + in: 'query', + name: 'max', + description: 'max value', + type: 'integer', + format: 'int64' + }, + { + in: 'query', + name: 'step', + description: 'step value', + type: 'integer', + format: 'int64' + }, + { + in: 'query', + name: 'steps', + description: 'steps value', + type: 'integer', + format: 'int64' + }, + { + in: 'query', + name: 'func', + description: 'comma separated aggregation functions', + type: 'string' } ], - "responses": { - "405": { - "description": "Invalid input" - }, - "200": { - "description": "successful operation", - "schema": { - "type": "array", - "items": { - "type": "object" + responses: { + '405': { + description: 'Invalid input' + }, + '200': { + description: 'successful operation', + schema: { + type: 'array', + items: { + type: 'object' } } } @@ -720,63 +644,57 @@ class SwaggerXc extends BaseRender { } }, [`/nc/${this.ctx.project_id}/api/${this.ctx.routeVersionLetter}/${this.ctx._tn}/distinct`]: { - "get": { - "tags": [ - `${this.ctx._tn}` - ], - "summary": "Get first one from filtered data", - "description": "", - "operationId": `${this.ctx._tn}Distinct`, - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "parameters": [ + get: { + tags: [`${this.ctx._tn}`], + summary: 'Get first one from filtered data', + description: '', + operationId: `${this.ctx._tn}Distinct`, + consumes: ['application/json'], + produces: ['application/json'], + parameters: [ { - "in": "query", - "name": "column_name", - "type": "string", - "description": "Column name" + in: 'query', + name: 'column_name', + type: 'string', + description: 'Column name' }, { - "in": "query", - "name": "where", - "type": "string", - "description": "Where expression" + in: 'query', + name: 'where', + type: 'string', + description: 'Where expression' }, { - "in": "query", - "name": "limit", - "description": "Page size limit", - "type": "integer", - "format": "int64" + in: 'query', + name: 'limit', + description: 'Page size limit', + type: 'integer', + format: 'int64' }, { - "in": "query", - "name": "offset", - "description": "Pagination offset", - "type": "integer", - "format": "int64" + in: 'query', + name: 'offset', + description: 'Pagination offset', + type: 'integer', + format: 'int64' }, { - "in": "query", - "name": "sort", - "description": "Comma separated sort fields", - "type": "string" + in: 'query', + name: 'sort', + description: 'Comma separated sort fields', + type: 'string' } ], - "responses": { - "405": { - "description": "Invalid input" - }, - "200": { - "description": "successful operation", - "schema": { - "type": "array", - "items": { - "type": "object" + responses: { + '405': { + description: 'Invalid input' + }, + '200': { + description: 'successful operation', + schema: { + type: 'array', + items: { + type: 'object' } } } @@ -784,84 +702,76 @@ class SwaggerXc extends BaseRender { } }, [`/nc/${this.ctx.project_id}/api/${this.ctx.routeVersionLetter}/${this.ctx._tn}/aggregate`]: { - "get": { - "tags": [ - `${this.ctx._tn}` - ], - "summary": "Get first one from filtered data", - "description": "", - "operationId": `${this.ctx._tn}Aggregate`, - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "parameters": [ + get: { + tags: [`${this.ctx._tn}`], + summary: 'Get first one from filtered data', + description: '', + operationId: `${this.ctx._tn}Aggregate`, + consumes: ['application/json'], + produces: ['application/json'], + parameters: [ { - "in": "query", - "name": "column_name", - "type": "string", - "description": "Column name" + in: 'query', + name: 'column_name', + type: 'string', + description: 'Column name' }, { - "in": "query", - "name": "func", - "type": "string", - "description": "Comma separated aggregate functions" + in: 'query', + name: 'func', + type: 'string', + description: 'Comma separated aggregate functions' }, { - "in": "query", - "name": "having", - "type": "string", - "description": "Having expression" + in: 'query', + name: 'having', + type: 'string', + description: 'Having expression' }, { - "in": "query", - "name": "fields", - "type": "string", - "description": "Comma separated fields from the model" + in: 'query', + name: 'fields', + type: 'string', + description: 'Comma separated fields from the model' }, { - "in": "query", - "name": "limit", - "description": "Page size limit", - "type": "integer", - "format": "int64" + in: 'query', + name: 'limit', + description: 'Page size limit', + type: 'integer', + format: 'int64' }, { - "in": "query", - "name": "offset", - "description": "Pagination offset", - "type": "integer", - "format": "int64" + in: 'query', + name: 'offset', + description: 'Pagination offset', + type: 'integer', + format: 'int64' }, { - "in": "query", - "name": "sort", - "description": "Comma separated sort fields", - "type": "string" + in: 'query', + name: 'sort', + description: 'Comma separated sort fields', + type: 'string' } ], - "responses": { - "405": { - "description": "Invalid input" - }, - "200": { - "description": "successful operation", - "schema": { - "$ref": `#/definitions/${this.ctx._tn}` + responses: { + '405': { + description: 'Invalid input' + }, + '200': { + description: 'successful operation', + schema: { + $ref: `#/definitions/${this.ctx._tn}` } } } } } }, - "definitions": this.getDefenitions(this.ctx) - } + definitions: this.getDefenitions(this.ctx) + }; } - - } export default SwaggerXc; diff --git a/packages/nocodb/src/lib/sqlMgr/code/routers/xc-ts/SwaggerXcBt.ts b/packages/nocodb/src/lib/sqlMgr/code/routers/xc-ts/SwaggerXcBt.ts index db827d93bb..a68dfb64b8 100644 --- a/packages/nocodb/src/lib/sqlMgr/code/routers/xc-ts/SwaggerXcBt.ts +++ b/packages/nocodb/src/lib/sqlMgr/code/routers/xc-ts/SwaggerXcBt.ts @@ -1,8 +1,6 @@ import BaseRender from '../../BaseRender'; - class SwaggerXcBt extends BaseRender { - /** * * @param dir @@ -12,16 +10,15 @@ class SwaggerXcBt extends BaseRender { * @param ctx.columns * @param ctx.relations */ - constructor({dir, filename, ctx}:any) { - super({dir, filename, ctx}); + constructor({ dir, filename, ctx }: any) { + super({ dir, filename, ctx }); } /** * Prepare variables used in code template */ prepare() { - - let data:any = {}; + let data: any = {}; /* example of simple variable */ data = this.ctx; @@ -36,7 +33,6 @@ class SwaggerXcBt extends BaseRender { }; return data; - } /** @@ -48,70 +44,65 @@ class SwaggerXcBt extends BaseRender { * @private */ _renderDefinitions(_args) { - const obj = {}; return JSON.stringify(obj); - } - getObject() { - return { - "tags": [ + tags: [ { - "name": `${this.ctx._tn}BelongsTo${this.ctx._rtn || this.ctx.rtn}`, - "description": "Everything about belongs to relation" + name: `${this.ctx._tn}BelongsTo${this.ctx._rtn || this.ctx.rtn}`, + description: 'Everything about belongs to relation' } ], - "paths": { - [`/nc/${this.ctx.project_id}/api/${this.ctx.routeVersionLetter}/${this.ctx._tn}/belongs/${this.ctx._rtn || this.ctx.rtn}`]: { - "get": { - "tags": [ - `${this.ctx._tn}BelongsTo${this.ctx._rtn || this.ctx.rtn}` - ], - "summary": `Get ${this.ctx._tn} list with ${this.ctx._rtn || this.ctx.rtn} parent`, - "description": "", - "operationId": `${this.ctx._tn}WithParent`, - "produces": [ - "application/json" - ], - "parameters": [ + paths: { + [`/nc/${this.ctx.project_id}/api/${this.ctx.routeVersionLetter}/${ + this.ctx._tn + }/belongs/${this.ctx._rtn || this.ctx.rtn}`]: { + get: { + tags: [`${this.ctx._tn}BelongsTo${this.ctx._rtn || this.ctx.rtn}`], + summary: `Get ${this.ctx._tn} list with ${this.ctx._rtn || + this.ctx.rtn} parent`, + description: '', + operationId: `${this.ctx._tn}WithParent`, + produces: ['application/json'], + parameters: [ { - "in": "query", - "name": "where", - "type": "String", - "description": "Where expression" + in: 'query', + name: 'where', + type: 'String', + description: 'Where expression' }, { - "in": "query", - "name": "limit", - "description": "Page size limit", - "type": "integer", - "format": "int64" + in: 'query', + name: 'limit', + description: 'Page size limit', + type: 'integer', + format: 'int64' }, { - "in": "query", - "name": "offset", - "description": "Pagination offset", - "type": "integer", - "format": "int64" + in: 'query', + name: 'offset', + description: 'Pagination offset', + type: 'integer', + format: 'int64' }, { - "in": "query", - "name": "sort", - "description": "Sort parameter", - "type": "string" + in: 'query', + name: 'sort', + description: 'Sort parameter', + type: 'string' } ], - "responses": { - "200": { - "description": "successful operation", - "schema": { - "type": "array", - "items": { - "type": "object" + responses: { + '200': { + description: 'successful operation', + schema: { + type: 'array', + items: { + type: 'object' } } } @@ -119,13 +110,9 @@ class SwaggerXcBt extends BaseRender { } } }, - "definitions": {} - } - + definitions: {} + }; } - - } - export default SwaggerXcBt; diff --git a/packages/nocodb/src/lib/sqlMgr/code/routers/xc-ts/SwaggerXcHm.ts b/packages/nocodb/src/lib/sqlMgr/code/routers/xc-ts/SwaggerXcHm.ts index 274f66d8cf..aef04eadc0 100644 --- a/packages/nocodb/src/lib/sqlMgr/code/routers/xc-ts/SwaggerXcHm.ts +++ b/packages/nocodb/src/lib/sqlMgr/code/routers/xc-ts/SwaggerXcHm.ts @@ -1,8 +1,6 @@ import BaseRender from '../../BaseRender'; - class SwaggerXcHm extends BaseRender { - /** * * @param dir @@ -12,16 +10,15 @@ class SwaggerXcHm extends BaseRender { * @param ctx.columns * @param ctx.relations */ - constructor({dir, filename, ctx}:any) { - super({dir, filename, ctx}); + constructor({ dir, filename, ctx }: any) { + super({ dir, filename, ctx }); } /** * Prepare variables used in code template */ prepare() { - - let data:any = {}; + let data: any = {}; /* example of simple variable */ data = this.ctx; @@ -36,7 +33,6 @@ class SwaggerXcHm extends BaseRender { }; return data; - } /** @@ -48,77 +44,68 @@ class SwaggerXcHm extends BaseRender { * @private */ _renderDefinitions(_args) { - const obj = {}; return JSON.stringify(obj); - } - - - getObject() { return { - "tags": [ + tags: [ { - "name": `${this.ctx._tn}HasMany${this.ctx._ctn}`, - "description": "Everything about has many relation" + name: `${this.ctx._tn}HasMany${this.ctx._ctn}`, + description: 'Everything about has many relation' } ], - "paths": { + paths: { [`/nc/${this.ctx.project_id}/api/${this.ctx.routeVersionLetter}/${this.ctx._tn}/has/${this.ctx._ctn}`]: { - "get": { - "tags": [ - `${this.ctx._tn}HasMany${this.ctx._ctn}` - ], - "summary": `Get ${this.ctx._tn} list with ${this.ctx._ctn} children`, - "description": "", - "operationId": `${this.ctx._tn}HasMany${this.ctx._ctn}List`, - "produces": [ - "application/json" - ], - "parameters": [ - { - "in": "query", - "name": "fields", - "type": "String", - "description": "Comma separated fields of model" + get: { + tags: [`${this.ctx._tn}HasMany${this.ctx._ctn}`], + summary: `Get ${this.ctx._tn} list with ${this.ctx._ctn} children`, + description: '', + operationId: `${this.ctx._tn}HasMany${this.ctx._ctn}List`, + produces: ['application/json'], + parameters: [ + { + in: 'query', + name: 'fields', + type: 'String', + description: 'Comma separated fields of model' }, { - "in": "query", - "name": "where", - "type": "String", - "description": "Where expression" + in: 'query', + name: 'where', + type: 'String', + description: 'Where expression' }, { - "in": "query", - "name": "limit", - "description": "page size limit", - "type": "integer", - "format": "int64" + in: 'query', + name: 'limit', + description: 'page size limit', + type: 'integer', + format: 'int64' }, { - "in": "query", - "name": "offset", - "description": "pagination offset", - "type": "integer", - "format": "int64" + in: 'query', + name: 'offset', + description: 'pagination offset', + type: 'integer', + format: 'int64' }, { - "in": "query", - "name": "sort", - "description": "sort parameter", - "type": "string" + in: 'query', + name: 'sort', + description: 'sort parameter', + type: 'string' } ], - "responses": { - "200": { - "description": "successful operation", - "schema": { - "type": "array", - "items": { - "type": "object" + responses: { + '200': { + description: 'successful operation', + schema: { + type: 'array', + items: { + type: 'object' } } } @@ -126,405 +113,366 @@ class SwaggerXcHm extends BaseRender { } }, [`/nc/${this.ctx.project_id}/api/${this.ctx.routeVersionLetter}/${this.ctx._tn}/{${this.ctx._tn}Id}/${this.ctx._ctn}`]: { - "get": { - "tags": [ - `${this.ctx._tn}HasMany${this.ctx._ctn}` - ], - "summary": `Find ${this.ctx._ctn} list by parent ${this.ctx._tn} id`, - "description": `Returns a single ${this.ctx._tn}`, - "operationId": `get${this.ctx._ctn}By${this.ctx._tn}Id`, - "produces": [ - "application/json" - ], - "parameters": [ - { - "name": `${this.ctx._tn}Id`, - "in": "path", - "description": `ID of ${this.ctx._tn} to return`, - "required": true, - "type": "integer", - "format": "int64" + get: { + tags: [`${this.ctx._tn}HasMany${this.ctx._ctn}`], + summary: `Find ${this.ctx._ctn} list by parent ${this.ctx._tn} id`, + description: `Returns a single ${this.ctx._tn}`, + operationId: `get${this.ctx._ctn}By${this.ctx._tn}Id`, + produces: ['application/json'], + parameters: [ + { + name: `${this.ctx._tn}Id`, + in: 'path', + description: `ID of ${this.ctx._tn} to return`, + required: true, + type: 'integer', + format: 'int64' }, { - "in": "query", - "name": "fields", - "type": "String", - "description": "Comma separated fields of model" + in: 'query', + name: 'fields', + type: 'String', + description: 'Comma separated fields of model' }, { - "in": "query", - "name": "where", - "type": "String", - "description": "Where expression" + in: 'query', + name: 'where', + type: 'String', + description: 'Where expression' }, { - "in": "query", - "name": "limit", - "description": "page size limit", - "type": "integer", - "format": "int64" + in: 'query', + name: 'limit', + description: 'page size limit', + type: 'integer', + format: 'int64' }, { - "in": "query", - "name": "offset", - "description": "pagination offset", - "type": "integer", - "format": "int64" + in: 'query', + name: 'offset', + description: 'pagination offset', + type: 'integer', + format: 'int64' }, { - "in": "query", - "name": "sort", - "description": "sort parameter", - "type": "string" + in: 'query', + name: 'sort', + description: 'sort parameter', + type: 'string' } ], - "responses": { - "200": { - "description": "successful operation", - "schema": { - "type": "array", - "items": "object" + responses: { + '200': { + description: 'successful operation', + schema: { + type: 'array', + items: 'object' } }, - "400": { - "description": "Invalid ID supplied" + '400': { + description: 'Invalid ID supplied' }, - "404": { - "description": `${this.ctx._tn} not found` + '404': { + description: `${this.ctx._tn} not found` } } }, - "post": { - "tags": [ - `${this.ctx._tn}HasMany${this.ctx._ctn}` - ], - "summary": `Insert ${this.ctx._ctn} under a parent ${this.ctx._tn}`, - "description": `Returns a single ${this.ctx._tn}`, - "operationId": `insert${this.ctx._ctn}By${this.ctx._tn}Id`, - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "parameters": [ - { - "in": "body", - "name": "body", - "description": `${this.ctx._ctn} object to insert`, - "required": true, - "schema": { - "type": "object" + post: { + tags: [`${this.ctx._tn}HasMany${this.ctx._ctn}`], + summary: `Insert ${this.ctx._ctn} under a parent ${this.ctx._tn}`, + description: `Returns a single ${this.ctx._tn}`, + operationId: `insert${this.ctx._ctn}By${this.ctx._tn}Id`, + consumes: ['application/json'], + produces: ['application/json'], + parameters: [ + { + in: 'body', + name: 'body', + description: `${this.ctx._ctn} object to insert`, + required: true, + schema: { + type: 'object' } }, { - "name": `${this.ctx._tn}Id`, - "in": "path", - "description": `ID of ${this.ctx._tn} to return`, - "required": true, - "type": "integer", - "format": "int64" + name: `${this.ctx._tn}Id`, + in: 'path', + description: `ID of ${this.ctx._tn} to return`, + required: true, + type: 'integer', + format: 'int64' } ], - "responses": { - "200": { - "description": "successful operation", - "schema": { - "type": "object" + responses: { + '200': { + description: 'successful operation', + schema: { + type: 'object' } } } } }, [`/nc/${this.ctx.project_id}/api/${this.ctx.routeVersionLetter}/${this.ctx._tn}/{${this.ctx._tn}Id}/${this.ctx._ctn}/{${this.ctx._ctn}Id}`]: { - "get": { - "tags": [ - `${this.ctx._tn}HasMany${this.ctx._ctn}` - ], - "summary": `Get by ${this.ctx._ctn} id parent ${this.ctx._tn} id`, - "description": `Returns a single ${this.ctx._tn}`, - "operationId": `get${this.ctx._ctn}ByIdAnd${this.ctx._tn}Id`, - "produces": [ - "application/json" - ], - "parameters": [ - { - "name": `${this.ctx._tn}Id`, - "in": "path", - "description": `ID of parent ${this.ctx._tn}`, - "required": true, - "type": "integer", - "format": "int64" + get: { + tags: [`${this.ctx._tn}HasMany${this.ctx._ctn}`], + summary: `Get by ${this.ctx._ctn} id parent ${this.ctx._tn} id`, + description: `Returns a single ${this.ctx._tn}`, + operationId: `get${this.ctx._ctn}ByIdAnd${this.ctx._tn}Id`, + produces: ['application/json'], + parameters: [ + { + name: `${this.ctx._tn}Id`, + in: 'path', + description: `ID of parent ${this.ctx._tn}`, + required: true, + type: 'integer', + format: 'int64' }, { - "name": `${this.ctx._ctn}Id`, - "in": "path", - "description": `ID of ${this.ctx._ctn}`, - "required": true, - "type": "integer", - "format": "int64" + name: `${this.ctx._ctn}Id`, + in: 'path', + description: `ID of ${this.ctx._ctn}`, + required: true, + type: 'integer', + format: 'int64' } ], - "responses": { - "200": { - "description": "successful operation", - "schema": { - "type": "array", - "items": "object" + responses: { + '200': { + description: 'successful operation', + schema: { + type: 'array', + items: 'object' } } } }, - "delete": { - "tags": [ - `${this.ctx._tn}HasMany${this.ctx._ctn}` - ], - "summary": `Delete by ${this.ctx._ctn} id parent ${this.ctx._tn} id`, - "description": `Returns a single ${this.ctx._tn}`, - "operationId": `delete${this.ctx._ctn}ByIdAnd${this.ctx._tn}Id`, - "produces": [ - "application/json" - ], - "parameters": [ - { - "name": `${this.ctx._tn}Id`, - "in": "path", - "description": `ID of parent ${this.ctx._tn}`, - "required": true, - "type": "integer", - "format": "int64" + delete: { + tags: [`${this.ctx._tn}HasMany${this.ctx._ctn}`], + summary: `Delete by ${this.ctx._ctn} id parent ${this.ctx._tn} id`, + description: `Returns a single ${this.ctx._tn}`, + operationId: `delete${this.ctx._ctn}ByIdAnd${this.ctx._tn}Id`, + produces: ['application/json'], + parameters: [ + { + name: `${this.ctx._tn}Id`, + in: 'path', + description: `ID of parent ${this.ctx._tn}`, + required: true, + type: 'integer', + format: 'int64' }, { - "name": `${this.ctx._ctn}Id`, - "in": "path", - "description": `ID of c${this.ctx._ctn}`, - "required": true, - "type": "integer", - "format": "int64" + name: `${this.ctx._ctn}Id`, + in: 'path', + description: `ID of c${this.ctx._ctn}`, + required: true, + type: 'integer', + format: 'int64' } ], - "responses": { - "200": { - "description": "successful operation" + responses: { + '200': { + description: 'successful operation' } } }, - "put": { - "tags": [ - `${this.ctx._tn}HasMany${this.ctx._ctn}` - ], - "summary": `Update ${this.ctx._ctn} under a parent ${this.ctx._tn}`, - "description": `Returns a single ${this.ctx._tn}`, - "operationId": `update${this.ctx._ctn}ByIdAnd${this.ctx._tn}Id`, - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "parameters": [ - { - "in": "body", - "name": "body", - "description": `${this.ctx._ctn} object to insert`, - "required": true, - "schema": { - "type": "object" + put: { + tags: [`${this.ctx._tn}HasMany${this.ctx._ctn}`], + summary: `Update ${this.ctx._ctn} under a parent ${this.ctx._tn}`, + description: `Returns a single ${this.ctx._tn}`, + operationId: `update${this.ctx._ctn}ByIdAnd${this.ctx._tn}Id`, + consumes: ['application/json'], + produces: ['application/json'], + parameters: [ + { + in: 'body', + name: 'body', + description: `${this.ctx._ctn} object to insert`, + required: true, + schema: { + type: 'object' } }, { - "name": `${this.ctx._tn}Id`, - "in": "path", - "description": `ID of ${this.ctx._tn} to return`, - "required": true, - "type": "integer", - "format": "int64" + name: `${this.ctx._tn}Id`, + in: 'path', + description: `ID of ${this.ctx._tn} to return`, + required: true, + type: 'integer', + format: 'int64' }, { - "name": `${this.ctx._ctn}Id`, - "in": "path", - "description": `ID of ${this.ctx._ctn}`, - "required": true, - "type": "integer", - "format": "int64" + name: `${this.ctx._ctn}Id`, + in: 'path', + description: `ID of ${this.ctx._ctn}`, + required: true, + type: 'integer', + format: 'int64' } ], - "responses": { - "200": { - "description": "successful operation", - "schema": { - "type": "object" + responses: { + '200': { + description: 'successful operation', + schema: { + type: 'object' } } } } }, [`/nc/${this.ctx.project_id}/api/${this.ctx.routeVersionLetter}/${this.ctx._tn}/{${this.ctx._tn}Id}/${this.ctx._ctn}/{${this.ctx._ctn}Id}/exists`]: { - "get": { - "tags": [ - `${this.ctx._tn}HasMany${this.ctx._ctn}` - ], - "summary": `Check row exists by ${this.ctx._ctn} id and parent ${this.ctx._tn} id`, - "description": "", - "operationId": `exists${this.ctx._ctn}ByIdAnd${this.ctx._tn}Id`, - "produces": [ - "application/json" - ], - "parameters": [ - { - "name": `${this.ctx._tn}Id`, - "in": "path", - "description": `ID of parent ${this.ctx._tn}`, - "required": true, - "type": "integer", - "format": "int64" + get: { + tags: [`${this.ctx._tn}HasMany${this.ctx._ctn}`], + summary: `Check row exists by ${this.ctx._ctn} id and parent ${this.ctx._tn} id`, + description: '', + operationId: `exists${this.ctx._ctn}ByIdAnd${this.ctx._tn}Id`, + produces: ['application/json'], + parameters: [ + { + name: `${this.ctx._tn}Id`, + in: 'path', + description: `ID of parent ${this.ctx._tn}`, + required: true, + type: 'integer', + format: 'int64' }, { - "name": `${this.ctx._ctn}Id`, - "in": "path", - "description": `ID of ${this.ctx._ctn}`, - "required": true, - "type": "integer", - "format": "int64" + name: `${this.ctx._ctn}Id`, + in: 'path', + description: `ID of ${this.ctx._ctn}`, + required: true, + type: 'integer', + format: 'int64' } ], - "responses": { - "200": { - "description": "successful operation", - "schema": { - "type": "boolean" + responses: { + '200': { + description: 'successful operation', + schema: { + type: 'boolean' } } } } }, [`/nc/${this.ctx.project_id}/api/${this.ctx.routeVersionLetter}/${this.ctx._tn}/{${this.ctx._tn}Id}/${this.ctx._ctn}/findOne`]: { - "get": { - "tags": [ - `${this.ctx._tn}HasMany${this.ctx._ctn}` - ], - "summary": `Find one ${this.ctx._ctn} by parent ${this.ctx._tn} id and filters`, - "description": "", - "operationId": `findOne${this.ctx._ctn}By${this.ctx._tn}Id`, - "produces": [ - "application/json" - ], - "parameters": [ - { - "name": `${this.ctx._tn}Id`, - "in": "path", - "description": `ID of parent ${this.ctx._tn}`, - "required": true, - "type": "integer", - "format": "int64" + get: { + tags: [`${this.ctx._tn}HasMany${this.ctx._ctn}`], + summary: `Find one ${this.ctx._ctn} by parent ${this.ctx._tn} id and filters`, + description: '', + operationId: `findOne${this.ctx._ctn}By${this.ctx._tn}Id`, + produces: ['application/json'], + parameters: [ + { + name: `${this.ctx._tn}Id`, + in: 'path', + description: `ID of parent ${this.ctx._tn}`, + required: true, + type: 'integer', + format: 'int64' }, { - "in": "query", - "name": "fields", - "type": "String", - "description": "Comma separated fields of model" + in: 'query', + name: 'fields', + type: 'String', + description: 'Comma separated fields of model' }, { - "in": "query", - "name": "where", - "type": "String", - "description": "Where expression" + in: 'query', + name: 'where', + type: 'String', + description: 'Where expression' }, { - "in": "query", - "name": "limit", - "description": "page size limit", - "type": "integer", - "format": "int64" + in: 'query', + name: 'limit', + description: 'page size limit', + type: 'integer', + format: 'int64' }, { - "in": "query", - "name": "offset", - "description": "pagination offset", - "type": "integer", - "format": "int64" + in: 'query', + name: 'offset', + description: 'pagination offset', + type: 'integer', + format: 'int64' }, { - "in": "query", - "name": "sort", - "description": "sort parameter", - "type": "string" + in: 'query', + name: 'sort', + description: 'sort parameter', + type: 'string' } ], - "responses": { - "200": { - "description": "successful operation", - "schema": { - "type": "object" + responses: { + '200': { + description: 'successful operation', + schema: { + type: 'object' } } } } }, [`/nc/${this.ctx.project_id}/api/${this.ctx.routeVersionLetter}/${this.ctx._tn}/{${this.ctx._tn}Id}/${this.ctx._ctn}/count`]: { - "get": { - "tags": [ - `${this.ctx._tn}HasMany${this.ctx._ctn}` - ], - "summary": `Get ${this.ctx._ctn} count by parent id and filter`, - "description": "", - "operationId": `getCountWithin${this.ctx._tn}Id`, - "produces": [ - "application/json" - ], - "parameters": [ - { - "name": `${this.ctx._tn}Id`, - "in": "path", - "description": `ID of parent ${this.ctx._tn}`, - "required": true, - "type": "integer", - "format": "int64" + get: { + tags: [`${this.ctx._tn}HasMany${this.ctx._ctn}`], + summary: `Get ${this.ctx._ctn} count by parent id and filter`, + description: '', + operationId: `getCountWithin${this.ctx._tn}Id`, + produces: ['application/json'], + parameters: [ + { + name: `${this.ctx._tn}Id`, + in: 'path', + description: `ID of parent ${this.ctx._tn}`, + required: true, + type: 'integer', + format: 'int64' }, { - "in": "query", - "name": "where", - "type": "String", - "description": "Where expression" + in: 'query', + name: 'where', + type: 'String', + description: 'Where expression' }, { - "in": "query", - "name": "limit", - "description": "page size limit", - "type": "integer", - "format": "int64" + in: 'query', + name: 'limit', + description: 'page size limit', + type: 'integer', + format: 'int64' }, { - "in": "query", - "name": "offset", - "description": "pagination offset", - "type": "integer", - "format": "int64" + in: 'query', + name: 'offset', + description: 'pagination offset', + type: 'integer', + format: 'int64' }, { - "in": "query", - "name": "sort", - "description": "sort parameter", - "type": "string" + in: 'query', + name: 'sort', + description: 'sort parameter', + type: 'string' } ], - "responses": { - "200": { - "description": "successful operation", - "schema": { - "type": "object" + responses: { + '200': { + description: 'successful operation', + schema: { + type: 'object' } } } } } }, - "definitions": {} - } - + definitions: {} + }; } - } - export default SwaggerXcHm; diff --git a/packages/nocodb/src/lib/sqlMgr/code/routes/xc-ts/ExpressXcTsRoutes.ts b/packages/nocodb/src/lib/sqlMgr/code/routes/xc-ts/ExpressXcTsRoutes.ts index 91e5f4fa69..02ffa270a9 100644 --- a/packages/nocodb/src/lib/sqlMgr/code/routes/xc-ts/ExpressXcTsRoutes.ts +++ b/packages/nocodb/src/lib/sqlMgr/code/routes/xc-ts/ExpressXcTsRoutes.ts @@ -1,8 +1,6 @@ -import BaseRender from "../../BaseRender"; +import BaseRender from '../../BaseRender'; class ExpressXcTsRoutes extends BaseRender { - - /** * * @param dir @@ -12,25 +10,22 @@ class ExpressXcTsRoutes extends BaseRender { * @param ctx.columns * @param ctx.relations */ - constructor({dir, filename, ctx}: any) { - super({dir, filename, ctx}); + constructor({ dir, filename, ctx }: any) { + super({ dir, filename, ctx }); } /** * Prepare variables used in code template */ prepare() { - let data: any = {}; /* example of simple variable */ data = this.ctx; return data; - } - getObject() { const ejsData: any = this.prepare(); const routes = [ @@ -43,13 +38,16 @@ class ExpressXcTsRoutes extends BaseRender { user: true, guest: true }, - functions: [` + functions: [ + ` async function(req, res){ const data = await req.model.list(req.query); res.json(data); } - `] - }, { + ` + ] + }, + { path: `/api/${this.ctx.routeVersionLetter}/${ejsData._tn}/findOne`, type: 'get', handler: ['findOne'], @@ -58,13 +56,16 @@ async function(req, res){ user: true, guest: true }, - functions: [` + functions: [ + ` async function(req, res){ const data = await req.model.findOne(req.query); res.json(data); } - `] - }, { + ` + ] + }, + { path: `/api/${this.ctx.routeVersionLetter}/${ejsData._tn}/m2mNotChildren/:assoc/:pid`, type: 'get', handler: ['m2mNotChildren'], @@ -73,9 +74,12 @@ async function(req, res){ user: true, guest: true }, - functions: [` - `] - }, { + functions: [ + ` + ` + ] + }, + { path: `/api/${this.ctx.routeVersionLetter}/${ejsData._tn}/groupby/:column_name`, type: 'get', handler: ['groupby'], @@ -84,7 +88,8 @@ async function(req, res){ user: true, guest: true }, - functions: [` + functions: [ + ` async function(req, res){ const data = await req.model.groupBy({ ...req.params, @@ -92,8 +97,10 @@ async function(req, res){ }); res.json(data); } - `] - }, { + ` + ] + }, + { path: `/api/${this.ctx.routeVersionLetter}/${ejsData._tn}/count`, type: 'get', handler: ['count'], @@ -102,15 +109,18 @@ async function(req, res){ user: true, guest: true }, - functions: [` + functions: [ + ` async function(req, res){ const data = await req.model.countByPk({ ...req.query }); res.json(data); } - `] - }, { + ` + ] + }, + { path: `/api/${this.ctx.routeVersionLetter}/${ejsData._tn}/bulk`, type: 'post', handler: ['bulkInsert'], @@ -119,13 +129,16 @@ async function(req, res){ user: true, guest: false }, - functions: [` + functions: [ + ` async function(req, res){ const data = await req.model.insertb(req.body); res.json(data); } - `] - }, { + ` + ] + }, + { path: `/api/${this.ctx.routeVersionLetter}/${ejsData._tn}/bulk`, type: 'put', handler: ['bulkUpdate'], @@ -134,13 +147,16 @@ async function(req, res){ user: true, guest: false }, - functions: [` + functions: [ + ` async function(req, res){ const data = await req.model.updateb(req.body); res.json(data); } - `] - }, { + ` + ] + }, + { path: `/api/${this.ctx.routeVersionLetter}/${ejsData._tn}/bulk`, type: 'delete', handler: ['bulkDelete'], @@ -149,13 +165,16 @@ async function(req, res){ user: true, guest: false }, - functions: [` + functions: [ + ` async function(req, res){ const data = await req.model.delb(req.body) res.json(data); } - `] - }, { + ` + ] + }, + { path: `/api/${this.ctx.routeVersionLetter}/${ejsData._tn}/:id/exists`, type: 'get', handler: ['exists'], @@ -164,13 +183,16 @@ async function(req, res){ user: true, guest: false }, - functions: [` + functions: [ + ` async function(req, res){ const data = await req.model.exists(req.params.id); res.json(data); } - `] - }, { + ` + ] + }, + { path: `/api/${this.ctx.routeVersionLetter}/${ejsData._tn}/distinct`, type: 'get', handler: ['distinct'], @@ -179,15 +201,18 @@ async function(req, res){ user: true, guest: true }, - functions: [` + functions: [ + ` async function(req, res){ const data = await req.model.distinct({ ...req.query }); res.json(data); } - `] - }, { + ` + ] + }, + { path: `/api/${this.ctx.routeVersionLetter}/${ejsData._tn}/distribute`, type: 'get', handler: ['distribute'], @@ -196,15 +221,18 @@ async function(req, res){ user: true, guest: true }, - functions: [` + functions: [ + ` async function(req, res){ const data = await req.model.distribution({ ...req.query }); res.json(data); } - `] - }, { + ` + ] + }, + { path: `/api/${this.ctx.routeVersionLetter}/${ejsData._tn}/aggregate`, type: 'get', handler: ['aggregate'], @@ -213,7 +241,8 @@ async function(req, res){ user: true, guest: true }, - functions: [` + functions: [ + ` async function(req, res){ const data = await req.model.aggregate({ ...req.params, @@ -221,8 +250,10 @@ async function(req, res){ }); res.json(data); } - `] - }, { + ` + ] + }, + { path: `/api/${this.ctx.routeVersionLetter}/${ejsData._tn}/groupby`, type: 'get', handler: ['groupby'], @@ -231,7 +262,8 @@ async function(req, res){ user: true, guest: true }, - functions: [` + functions: [ + ` async function(req, res){ const data = await req.model.groupBy({ ...req.params, @@ -239,8 +271,10 @@ async function(req, res){ }); res.json(data); } - `] - }, { + ` + ] + }, + { path: `/api/${this.ctx.routeVersionLetter}/${ejsData._tn}/:id`, type: 'get', handler: ['get'], @@ -249,13 +283,16 @@ async function(req, res){ user: true, guest: true }, - functions: [` + functions: [ + ` async function(req, res){ const data = await req.model.readByPk(req.params.id); res.json(data); } - `] - }, { + ` + ] + }, + { path: `/api/${this.ctx.routeVersionLetter}/${ejsData._tn}`, type: 'post', handler: ['create'], @@ -264,13 +301,16 @@ async function(req, res){ user: true, guest: false }, - functions: [` + functions: [ + ` async function(req, res){ const data = await req.model.insert(req.body, null, req); res.json(data); } - `] - }, { + ` + ] + }, + { path: `/api/${this.ctx.routeVersionLetter}/${ejsData._tn}/:id`, type: 'put', handler: ['update'], @@ -279,13 +319,16 @@ async function(req, res){ user: true, guest: false }, - functions: [` + functions: [ + ` async function(req, res){ const data = await req.model.updateByPk(req.params.id, req.body, null, req); res.json(data); } - `] - }, { + ` + ] + }, + { path: `/api/${this.ctx.routeVersionLetter}/${ejsData._tn}/:id`, type: 'delete', handler: ['delete'], @@ -294,24 +337,30 @@ async function(req, res){ user: true, guest: false }, - functions: [` + functions: [ + ` async function(req, res){ const data = await req.model.delByPk(req.params.id, null, req); res.json(data); } - `] + ` + ] } ]; if (this.ctx.type === 'view') { - return routes.filter(({type, handler}) => type === 'get' && !handler.includes('exists') && !handler.includes('get')) + return routes.filter( + ({ type, handler }) => + type === 'get' && + !handler.includes('exists') && + !handler.includes('get') + ); } return routes; } - getObjectWithoutFunctions() { - return this.getObject().map(({functions, ...rest}) => rest) + return this.getObject().map(({ functions, ...rest }) => rest); } } diff --git a/packages/nocodb/src/lib/sqlMgr/code/routes/xc-ts/ExpressXcTsRoutesBt.ts b/packages/nocodb/src/lib/sqlMgr/code/routes/xc-ts/ExpressXcTsRoutesBt.ts index 37b28b7f10..2d40ab68a6 100644 --- a/packages/nocodb/src/lib/sqlMgr/code/routes/xc-ts/ExpressXcTsRoutesBt.ts +++ b/packages/nocodb/src/lib/sqlMgr/code/routes/xc-ts/ExpressXcTsRoutesBt.ts @@ -1,46 +1,45 @@ -import BaseRender from "../../BaseRender"; +import BaseRender from '../../BaseRender'; class ExpressXcTsRoutesBt extends BaseRender { - - /** - * - * @param dir - * @param filename - * @param ct - * @param ctx.tn - * @param ctx.columns - * @param ctx.relations - */ - constructor({dir, filename, ctx}:any) { - super({dir, filename, ctx}); - } - - /** - * Prepare variables used in code template - */ - prepare() { - - let data:any = {}; - - /* example of simple variable */ - data = this.ctx; - - return data; - } - - getObject() { - const ejsData = this.prepare(); - return [ - { - path: `/api/${this.ctx.routeVersionLetter}/${ejsData._tn}/belongs/${ejsData._rtn}`, - type: 'get', - handler: ['list'], - acl: { - admin: true, - user: true, - guest: true - }, - functions:[` + /** + * + * @param dir + * @param filename + * @param ct + * @param ctx.tn + * @param ctx.columns + * @param ctx.relations + */ + constructor({ dir, filename, ctx }: any) { + super({ dir, filename, ctx }); + } + + /** + * Prepare variables used in code template + */ + prepare() { + let data: any = {}; + + /* example of simple variable */ + data = this.ctx; + + return data; + } + + getObject() { + const ejsData = this.prepare(); + return [ + { + path: `/api/${this.ctx.routeVersionLetter}/${ejsData._tn}/belongs/${ejsData._rtn}`, + type: 'get', + handler: ['list'], + acl: { + admin: true, + user: true, + guest: true + }, + functions: [ + ` async function(req, res){ const data = await req.childModel.belongsTo({ parents: req.parentModel.tn, @@ -48,16 +47,15 @@ async function(req, res){ }); res.json(data); } - `] - } + ` ] - } - - getObjectWithoutFunctions() { - return this.getObject().map(({functions, ...rest}) => rest) - } + } + ]; + } + getObjectWithoutFunctions() { + return this.getObject().map(({ functions, ...rest }) => rest); + } } - export default ExpressXcTsRoutesBt; diff --git a/packages/nocodb/src/lib/sqlMgr/code/routes/xc-ts/ExpressXcTsRoutesHm.ts b/packages/nocodb/src/lib/sqlMgr/code/routes/xc-ts/ExpressXcTsRoutesHm.ts index 6bfebe8827..065888a382 100644 --- a/packages/nocodb/src/lib/sqlMgr/code/routes/xc-ts/ExpressXcTsRoutesHm.ts +++ b/packages/nocodb/src/lib/sqlMgr/code/routes/xc-ts/ExpressXcTsRoutesHm.ts @@ -1,7 +1,6 @@ -import BaseRender from "../../BaseRender"; +import BaseRender from '../../BaseRender'; class ExpressXcTsRoutesHm extends BaseRender { - /** * * @param dir @@ -11,25 +10,22 @@ class ExpressXcTsRoutesHm extends BaseRender { * @param ctx.columns * @param ctx.relations */ - constructor({dir, filename, ctx}: any) { - super({dir, filename, ctx}); + constructor({ dir, filename, ctx }: any) { + super({ dir, filename, ctx }); } /** * Prepare variables used in code template */ public prepare(): any { - let data = {}; /* example of simple variable */ data = this.ctx; return data; - } - public getObject() { const ejsData: any = this.prepare(); return [ @@ -42,7 +38,8 @@ class ExpressXcTsRoutesHm extends BaseRender { user: true, guest: true }, - functions: [` + functions: [ + ` async function(req, res){ const data = await req.parentModel.hasManyList({ ...req.query, @@ -50,7 +47,8 @@ async function(req, res){ }); res.json(data); } - `] + ` + ] }, { path: `/api/v1/${ejsData._tn}/:parentId/${ejsData._ctn}`, @@ -61,7 +59,8 @@ async function(req, res){ user: true, guest: true }, - functions: [` + functions: [ + ` async function(req, res){ const data = await req.parentModel.hasManyChildren({ child: req.childModel.tn, @@ -70,7 +69,8 @@ async function(req, res){ }) res.json(data); } - `] + ` + ] }, { path: `/api/v1/${ejsData._tn}/:parentId/${ejsData._ctn}`, @@ -81,7 +81,8 @@ async function(req, res){ user: true, guest: false }, - functions: [` + functions: [ + ` async function(req, res){ const data = await req.childModel.insertByFk({ parentId: req.params.parentId, @@ -90,7 +91,8 @@ async function(req, res){ }); res.json(data); } - `] + ` + ] }, { path: `/api/v1/${ejsData._tn}/:parentId/${ejsData._ctn}/findOne`, @@ -101,7 +103,8 @@ async function(req, res){ user: true, guest: true }, - functions: [` + functions: [ + ` async function(req, res){ const data = await req.childModel.findOneByFk({ parentId: req.params.parentId, @@ -110,7 +113,8 @@ async function(req, res){ }); res.json(data); } - `] + ` + ] }, { path: `/api/v1/${ejsData._tn}/:parentId/${ejsData._ctn}/count`, @@ -121,7 +125,8 @@ async function(req, res){ user: true, guest: true }, - functions: [` + functions: [ + ` async function(req, res){ const data = await req.childModel.countByFk({ parentId: req.params.parentId, @@ -130,7 +135,8 @@ async function(req, res){ }); res.json(data); } - `] + ` + ] }, { path: `/api/v1/${ejsData._tn}/:parentId/${ejsData._ctn}/:id`, @@ -141,7 +147,8 @@ async function(req, res){ user: true, guest: true }, - functions: [` + functions: [ + ` async function(req, res){ const data = await req.childModel.readByFk({ parentId: req.params.parentId, @@ -150,7 +157,8 @@ async function(req, res){ }); res.json(data); } - `] + ` + ] }, { path: `/api/v1/${ejsData._tn}/:parentId/${ejsData._ctn}/:id`, @@ -161,7 +169,8 @@ async function(req, res){ user: true, guest: false }, - functions: [` + functions: [ + ` async function(req, res){ const data = await req.childModel.updateByFk({ parentId: req.params.parentId, @@ -171,7 +180,8 @@ async function(req, res){ }); res.json(data); } - `] + ` + ] }, { path: `/api/v1/${ejsData._tn}/:parentId/${ejsData._ctn}/:id`, @@ -182,7 +192,8 @@ async function(req, res){ user: true, guest: false }, - functions: [` + functions: [ + ` async function(req, res){ const data = await req.childModel.delByFk({ parentId: req.params.parentId, @@ -191,7 +202,8 @@ async function(req, res){ }); res.json(data); } - `] + ` + ] }, { path: `/api/v1/${ejsData._tn}/:parentId/${ejsData._ctn}/:id/exists`, @@ -202,7 +214,8 @@ async function(req, res){ user: true, guest: true }, - functions: [` + functions: [ + ` async function(req, res){ const data = await req.childModel.existsByFk({ parentId: req.params.parentId, @@ -212,16 +225,15 @@ async function(req, res){ }); res.json(data); } - `] + ` + ] } - ] + ]; } - public getObjectWithoutFunctions() { - return this.getObject().map(({functions, ...rest}) => rest) + return this.getObject().map(({ functions, ...rest }) => rest); } } - export default ExpressXcTsRoutesHm; diff --git a/packages/nocodb/src/lib/utils/Lang.ts b/packages/nocodb/src/lib/utils/Lang.ts index d52d0c525f..857eaac6bb 100644 --- a/packages/nocodb/src/lib/utils/Lang.ts +++ b/packages/nocodb/src/lib/utils/Lang.ts @@ -4,15 +4,16 @@ import english from './english.json'; import translated from './translated.json'; /* Converted from : https://smodin.me/translate-one-text-into-multiple-languages -* Enter database host name || Choose SQL Database type || Enter database username || Enter database password || Enter database port number || Enter database/schema name || Enter API type to generate || How do you want to run it -* */ + * Enter database host name || Choose SQL Database type || Enter database username || Enter database password || Enter database port number || Enter database/schema name || Enter API type to generate || How do you want to run it + * */ const formattedTranslate: any = {}; -for (const {symbol, text} of ([english, ...translated].sort((a: any, b: any) => a.symbol.localeCompare(b.symbol)) as any[])) { - formattedTranslate [symbol] = text.split(/\s*\|\|\s*/); +for (const { symbol, text } of [english, ...translated].sort((a: any, b: any) => + a.symbol.localeCompare(b.symbol) +) as any[]) { + formattedTranslate[symbol] = text.split(/\s*\|\|\s*/); } - const dummy: any = new Date(); const offset: any = -dummy.getTimezoneOffset(); const locale: string = offset === 330 ? 'en-IN' : osLocale.sync(); @@ -21,14 +22,10 @@ enum STR { SLOGAN } - class Lang { - // @ts-ignore public static getString(str: STR): string { - switch (locale) { - case 'en': case 'en-GB': case 'en-AU': @@ -50,7 +47,6 @@ class Lang { // case 'en-IN': // break; - case 'de': case 'de-DE': case 'de-CH': @@ -103,19 +99,16 @@ class Lang { case 'pt': case 'pt-BR': case 'pt-PT': - return `${formattedTranslate?.pt?.[str]}`; case 'ru': case 'ru-RU': return `${formattedTranslate?.ru?.[str]}`; - case 'sv': case 'sv-SE': return `${formattedTranslate?.sv?.[str]}`; - case 'th': case 'th-TH': return `${formattedTranslate?.th?.[str]}`; @@ -124,12 +117,10 @@ class Lang { case 'tl-PH': return `${formattedTranslate?.tl?.[str]}`; - case 'tr': case 'tr-TR': return `${formattedTranslate?.tr?.[str]}`; - case 'uk': case 'uk-UA': return `${formattedTranslate?.uk?.[str]}`; @@ -138,16 +129,11 @@ class Lang { case 'vi-VN': return `${formattedTranslate?.vi?.[str]}`; } - } - - } export default Lang; -export { - STR -}; +export { STR }; /** * @copyright Copyright (c) 2021, Xgene Cloud Ltd * diff --git a/packages/nocodb/src/lib/utils/NcConfigFactory.ts b/packages/nocodb/src/lib/utils/NcConfigFactory.ts index c76f6f4515..c2dbd5a624 100644 --- a/packages/nocodb/src/lib/utils/NcConfigFactory.ts +++ b/packages/nocodb/src/lib/utils/NcConfigFactory.ts @@ -1,58 +1,66 @@ -import {SqlClientFactory} from 'nc-help'; +import { SqlClientFactory } from 'nc-help'; import fs from 'fs'; -import parseDbUrl from "parse-database-url"; - -import {AuthConfig, DbConfig, MailerConfig, NcConfig} from "../../interface/config"; -import * as path from "path"; - -const {uniqueNamesGenerator, starWars, adjectives, animals} = require('unique-names-generator'); +import parseDbUrl from 'parse-database-url'; + +import { + AuthConfig, + DbConfig, + MailerConfig, + NcConfig +} from '../../interface/config'; +import * as path from 'path'; + +const { + uniqueNamesGenerator, + starWars, + adjectives, + animals +} = require('unique-names-generator'); const driverClientMapping = { mysql: 'mysql2', postgres: 'pg', sqlite: 'sqlite3', - mssql: 'mssql', -} + mssql: 'mssql' +}; const defaultClientPortMapping = { mysql: 3306, mysql2: 3306, postgres: 5432, pg: 5432, - mssql: 1433, -} + mssql: 1433 +}; const defaultConnectionConfig = { // timezone: 'UTC', // dateStrings: true -} - +}; export default class NcConfigFactory implements NcConfig { - - public static make(): NcConfig { - this.jdbcToXcUrl(); const config = new NcConfigFactory(); - config.auth = { jwt: { secret: process.env.NC_AUTH_JWT_SECRET } }; - config.port = +(process?.env?.PORT ?? 8080); config.env = '_noco'; // process.env?.NODE_ENV || 'dev'; config.workingEnv = '_noco'; // process.env?.NODE_ENV || 'dev'; config.toolDir = this.getToolDir(); - config.projectType = config?.envs?.[config.workingEnv]?.db?.[0]?.meta?.api?.type || 'rest'; + config.projectType = + config?.envs?.[config.workingEnv]?.db?.[0]?.meta?.api?.type || 'rest'; if (config.meta?.db?.connection?.filename) { - config.meta.db.connection.filename = path.join(config.toolDir, config.meta.db.connection.filename) + config.meta.db.connection.filename = path.join( + config.toolDir, + config.meta.db.connection.filename + ); } if (process.env.NC_DB_JSON) { @@ -67,10 +75,9 @@ export default class NcConfigFactory implements NcConfig { const fileContent = fs.readFileSync(filePath, { encoding: 'utf8' }); config.meta.db = JSON.parse(fileContent); } else if (process.env.NC_DB) { - config.meta.db = this.metaUrlToDbConfig(process.env.NC_DB) + config.meta.db = this.metaUrlToDbConfig(process.env.NC_DB); } - if (process.env.NC_TRY) { config.try = true; config.meta.db = { @@ -85,14 +92,12 @@ export default class NcConfigFactory implements NcConfig { } as any; } - if (process.env.NC_PUBLIC_URL) { config.envs['_noco'].publicUrl = process.env.NC_PUBLIC_URL; // config.envs[process.env.NODE_ENV || 'dev'].publicUrl = process.env.NC_PUBLIC_URL; config.publicUrl = process.env.NC_PUBLIC_URL; } - if (process.env.NC_DASHBOARD_URL) { config.dashboardPath = process.env.NC_DASHBOARD_URL; } @@ -100,22 +105,26 @@ export default class NcConfigFactory implements NcConfig { return config; } - public static makeOld(): NcConfig { const config = new NcConfigFactory(); - const dbUrls = Object.keys(process.env).filter(envKey => envKey.startsWith('NC_DB_URL')); + const dbUrls = Object.keys(process.env).filter(envKey => + envKey.startsWith('NC_DB_URL') + ); // if (!dbUrls.length) { // return null // } for (const key of dbUrls.sort()) { - const dbConfig = this.urlToDbConfig(process?.env?.[key], key.slice(9), config); + const dbConfig = this.urlToDbConfig( + process?.env?.[key], + key.slice(9), + config + ); config.envs['_noco'].db.push(dbConfig); // config.envs[process.env.NODE_ENV || 'dev'].db.push(dbConfig); } - if (process.env.NC_AUTH_ADMIN_SECRET) { config.auth = { masterKey: { @@ -126,23 +135,23 @@ export default class NcConfigFactory implements NcConfig { config.auth = { disabled: true }; - // } else if (config?.envs?.[process.env.NODE_ENV || 'dev']?.db?.[0]) { + // } else if (config?.envs?.[process.env.NODE_ENV || 'dev']?.db?.[0]) { } else if (config?.envs?.['_noco']?.db?.[0]) { config.auth = { jwt: { // dbAlias: process.env.NC_AUTH_JWT_DB_ALIAS || config.envs[process.env.NODE_ENV || 'dev'].db[0].meta.dbAlias, - dbAlias: process.env.NC_AUTH_JWT_DB_ALIAS || config.envs['_noco'].db[0].meta.dbAlias, + dbAlias: + process.env.NC_AUTH_JWT_DB_ALIAS || + config.envs['_noco'].db[0].meta.dbAlias, secret: process.env.NC_AUTH_JWT_SECRET } }; } - if (process.env.NC_DB) { - config.meta.db = this.metaUrlToDbConfig(process.env.NC_DB) + config.meta.db = this.metaUrlToDbConfig(process.env.NC_DB); } - if (process.env.NC_TRY) { config.try = true; config.meta.db = { @@ -157,37 +166,35 @@ export default class NcConfigFactory implements NcConfig { } as any; } - if (process.env.NC_MAILER) { config.mailer = { from: process.env.NC_MAILER_FROM, options: { - "host": process.env.NC_MAILER_HOST, - "port": parseInt(process.env.NC_MAILER_PORT, 10), - "secure": process.env.NC_MAILER_SECURE === 'true', - "auth": { - "user": process.env.NC_MAILER_USER, - "pass": process.env.NC_MAILER_PASS + host: process.env.NC_MAILER_HOST, + port: parseInt(process.env.NC_MAILER_PORT, 10), + secure: process.env.NC_MAILER_SECURE === 'true', + auth: { + user: process.env.NC_MAILER_USER, + pass: process.env.NC_MAILER_PASS } } - } + }; } - if (process.env.NC_PUBLIC_URL) { config.envs['_noco'].publicUrl = process.env.NC_PUBLIC_URL; // config.envs[process.env.NODE_ENV || 'dev'].publicUrl = process.env.NC_PUBLIC_URL; config.publicUrl = process.env.NC_PUBLIC_URL; } - config.port = +(process?.env?.PORT ?? 8080); // config.env = process.env?.NODE_ENV || 'dev'; // config.workingEnv = process.env?.NODE_ENV || 'dev'; config.env = '_noco'; config.workingEnv = '_noco'; config.toolDir = this.getToolDir(); - config.projectType = config?.envs?.[config.workingEnv]?.db?.[0]?.meta?.api?.type || 'rest'; + config.projectType = + config?.envs?.[config.workingEnv]?.db?.[0]?.meta?.api?.type || 'rest'; return config; } @@ -197,7 +204,9 @@ export default class NcConfigFactory implements NcConfig { } public static hasDbUrl(): boolean { - return Object.keys(process.env).some(envKey => envKey.startsWith('NC_DB_URL')); + return Object.keys(process.env).some(envKey => + envKey.startsWith('NC_DB_URL') + ); } public static makeFromUrls(urls: string[]): NcConfig { @@ -213,8 +222,12 @@ export default class NcConfigFactory implements NcConfig { return config; } - - public static urlToDbConfig(urlString: string, key?: string, config?: NcConfigFactory, type?: string): DbConfig { + public static urlToDbConfig( + urlString: string, + key?: string, + config?: NcConfigFactory, + type?: string + ): DbConfig { const url = new URL(urlString); let dbConfig: DbConfig; @@ -222,82 +235,98 @@ export default class NcConfigFactory implements NcConfig { if (url.protocol.startsWith('sqlite3')) { dbConfig = { client: 'sqlite3', - "connection": { - "client": "sqlite3", - "connection": { - "filename": url.searchParams.get('d') || url.searchParams.get('database') + connection: { + client: 'sqlite3', + connection: { + filename: + url.searchParams.get('d') || url.searchParams.get('database') }, - "database": url.searchParams.get('d') || url.searchParams.get('database'), - "useNullAsDefault": true - }, + database: + url.searchParams.get('d') || url.searchParams.get('database'), + useNullAsDefault: true + } } as any; } else { dbConfig = { client: url.protocol.replace(':', ''), - "connection": { + connection: { ...defaultConnectionConfig, - database: url.searchParams.get('d') || url.searchParams.get('database'), - "host": url.hostname, - "password": url.searchParams.get('p') || url.searchParams.get('password'), - "port": +url.port, - 'user': url.searchParams.get('u') || url.searchParams.get('user'), + database: + url.searchParams.get('d') || url.searchParams.get('database'), + host: url.hostname, + password: + url.searchParams.get('p') || url.searchParams.get('password'), + port: +url.port, + user: url.searchParams.get('u') || url.searchParams.get('user') }, // pool: { // min: 1, // max: 1 // }, - acquireConnectionTimeout: 600000, + acquireConnectionTimeout: 600000 } as any; if (process.env.NODE_TLS_REJECT_UNAUTHORIZED) { dbConfig.connection.ssl = true; } - - if (url.searchParams.get('keyFilePath') && url.searchParams.get('certFilePath') && url.searchParams.get('caFilePath')) { + if ( + url.searchParams.get('keyFilePath') && + url.searchParams.get('certFilePath') && + url.searchParams.get('caFilePath') + ) { dbConfig.connection.ssl = { keyFilePath: url.searchParams.get('keyFilePath'), certFilePath: url.searchParams.get('certFilePath'), - caFilePath: url.searchParams.get('caFilePath'), - } + caFilePath: url.searchParams.get('caFilePath') + }; } - - } if (config && !config.title) { - config.title = url.searchParams.get('t') || url.searchParams.get('title') || this.generateRandomTitle(); + config.title = + url.searchParams.get('t') || + url.searchParams.get('title') || + this.generateRandomTitle(); } Object.assign(dbConfig, { meta: { tn: 'nc_evolutions', - allSchemas: !!url.searchParams.get('allSchemas') || !(url.searchParams.get('d') || url.searchParams.get('database')), + allSchemas: + !!url.searchParams.get('allSchemas') || + !(url.searchParams.get('d') || url.searchParams.get('database')), api: { prefix: url.searchParams.get('apiPrefix') || '', swagger: true, - type: type || ((url.searchParams.get('api') || url.searchParams.get('a')) as any) || "rest", + type: + type || + ((url.searchParams.get('api') || + url.searchParams.get('a')) as any) || + 'rest' }, dbAlias: url.searchParams.get('dbAlias') || `db${key}`, metaTables: 'db', migrations: { disabled: false, - name: "nc_evolutions" + name: 'nc_evolutions' } } - }) - + }); return dbConfig; } private static generateRandomTitle(): string { return uniqueNamesGenerator({ - dictionaries: [[starWars], [adjectives, animals]][Math.floor(Math.random() * 2)] - }).toLowerCase().replace(/[ -]/g, '_'); + dictionaries: [[starWars], [adjectives, animals]][ + Math.floor(Math.random() * 2) + ] + }) + .toLowerCase() + .replace(/[ -]/g, '_'); } - static metaUrlToDbConfig(urlString) { const url = new URL(urlString); @@ -306,33 +335,40 @@ export default class NcConfigFactory implements NcConfig { if (url.protocol.startsWith('sqlite3')) { const db = url.searchParams.get('d') || url.searchParams.get('database'); dbConfig = { - "client": "sqlite3", - "connection": { - "filename": db - }, ...(db === ':memory:' ? { - pool: { - min: 1, - max: 1, - // disposeTimeout: 360000*1000, - idleTimeoutMillis: 360000 * 1000 - } - } : {}) - } + client: 'sqlite3', + connection: { + filename: db + }, + ...(db === ':memory:' + ? { + pool: { + min: 1, + max: 1, + // disposeTimeout: 360000*1000, + idleTimeoutMillis: 360000 * 1000 + } + } + : {}) + }; } else { dbConfig = { client: url.protocol.replace(':', ''), - "connection": { + connection: { ...defaultConnectionConfig, - database: url.searchParams.get('d') || url.searchParams.get('database'), - "host": url.hostname, - "password": url.searchParams.get('p') || url.searchParams.get('password'), - "port": +url.port, - 'user': url.searchParams.get('u') || url.searchParams.get('user'), + database: + url.searchParams.get('d') || url.searchParams.get('database'), + host: url.hostname, + password: + url.searchParams.get('p') || url.searchParams.get('password'), + port: +url.port, + user: url.searchParams.get('u') || url.searchParams.get('user') }, acquireConnectionTimeout: 600000, - ...(url.searchParams.has('search_path') ? { - searchPath: url.searchParams.get('search_path').split(',') - } : {}) + ...(url.searchParams.has('search_path') + ? { + searchPath: url.searchParams.get('search_path').split(',') + } + : {}) }; if (process.env.NODE_TLS_REJECT_UNAUTHORIZED) { dbConfig.connection.ssl = true; @@ -348,25 +384,32 @@ export default class NcConfigFactory implements NcConfig { value = +value; } // todo: implement config read from JSON file or JSON env val read - if (!['password', 'p', 'database', 'd', 'user', 'u', 'search_path'].includes(key)) { + if ( + ![ + 'password', + 'p', + 'database', + 'd', + 'user', + 'u', + 'search_path' + ].includes(key) + ) { key.split('.').reduce((obj, k, i, arr) => { - return obj[k] = i === arr.length - 1 ? value : (obj[k] || {}) + return (obj[k] = i === arr.length - 1 ? value : obj[k] || {}); }, dbConfig); } - }) - + }); - return dbConfig + return dbConfig; } - public static makeProjectConfigFromUrl(url, type?: string): NcConfig { const config = new NcConfigFactory(); const dbConfig = this.urlToDbConfig(url, '', config, type); // config.envs[process.env.NODE_ENV || 'dev'].db.push(dbConfig); config.envs['_noco'].db.push(dbConfig); - if (process.env.NC_AUTH_ADMIN_SECRET) { config.auth = { masterKey: { @@ -377,23 +420,23 @@ export default class NcConfigFactory implements NcConfig { config.auth = { disabled: true }; - // } else if (config?.envs?.[process.env.NODE_ENV || 'dev']?.db?.[0]) { + // } else if (config?.envs?.[process.env.NODE_ENV || 'dev']?.db?.[0]) { } else if (config?.envs?.['_noco']?.db?.[0]) { config.auth = { jwt: { // dbAlias: process.env.NC_AUTH_JWT_DB_ALIAS || config.envs[process.env.NODE_ENV || 'dev'].db[0].meta.dbAlias, - dbAlias: process.env.NC_AUTH_JWT_DB_ALIAS || config.envs['_noco'].db[0].meta.dbAlias, + dbAlias: + process.env.NC_AUTH_JWT_DB_ALIAS || + config.envs['_noco'].db[0].meta.dbAlias, secret: process.env.NC_AUTH_JWT_SECRET } }; } - if (process.env.NC_DB) { - config.meta.db = this.metaUrlToDbConfig(process.env.NC_DB) + config.meta.db = this.metaUrlToDbConfig(process.env.NC_DB); } - if (process.env.NC_TRY) { config.try = true; config.meta.db = { @@ -408,55 +451,57 @@ export default class NcConfigFactory implements NcConfig { } as any; } - if (process.env.NC_MAILER) { config.mailer = { from: process.env.NC_MAILER_FROM, options: { - "host": process.env.NC_MAILER_HOST, - "port": parseInt(process.env.NC_MAILER_PORT, 10), - "secure": process.env.NC_MAILER_SECURE === 'true', - "auth": { - "user": process.env.NC_MAILER_USER, - "pass": process.env.NC_MAILER_PASS + host: process.env.NC_MAILER_HOST, + port: parseInt(process.env.NC_MAILER_PORT, 10), + secure: process.env.NC_MAILER_SECURE === 'true', + auth: { + user: process.env.NC_MAILER_USER, + pass: process.env.NC_MAILER_PASS } } - } + }; } - if (process.env.NC_PUBLIC_URL) { // config.envs[process.env.NODE_ENV || 'dev'].publicUrl = process.env.NC_PUBLIC_URL; config.envs['_noco'].publicUrl = process.env.NC_PUBLIC_URL; config.publicUrl = process.env.NC_PUBLIC_URL; } - config.port = +(process?.env?.PORT ?? 8080); // config.env = process.env?.NODE_ENV || 'dev'; // config.workingEnv = process.env?.NODE_ENV || 'dev'; config.env = '_noco'; config.workingEnv = '_noco'; config.toolDir = this.getToolDir(); - config.projectType = type || config?.envs?.[config.workingEnv]?.db?.[0]?.meta?.api?.type || 'rest'; + config.projectType = + type || + config?.envs?.[config.workingEnv]?.db?.[0]?.meta?.api?.type || + 'rest'; return config; } - - public static makeProjectConfigFromConnection(dbConnectionConfig: any, type?: string): NcConfig { + public static makeProjectConfigFromConnection( + dbConnectionConfig: any, + type?: string + ): NcConfig { const config = new NcConfigFactory(); let dbConfig = dbConnectionConfig; if (dbConfig.client === 'sqlite3') { dbConfig = { client: 'sqlite3', - "connection": { + connection: { ...dbConnectionConfig, - "database": dbConnectionConfig.connection.filename, - "useNullAsDefault": true - }, - } + database: dbConnectionConfig.connection.filename, + useNullAsDefault: true + } + }; } // todo: @@ -467,22 +512,20 @@ export default class NcConfigFactory implements NcConfig { api: { prefix: '', swagger: true, - type: type || "rest", + type: type || 'rest' }, dbAlias: `db${key}`, metaTables: 'db', migrations: { disabled: false, - name: "nc_evolutions" + name: 'nc_evolutions' } } - }) - + }); // config.envs[process.env.NODE_ENV || 'dev'].db.push(dbConfig); config.envs['_noco'].db.push(dbConfig); - if (process.env.NC_AUTH_ADMIN_SECRET) { config.auth = { masterKey: { @@ -493,23 +536,23 @@ export default class NcConfigFactory implements NcConfig { config.auth = { disabled: true }; - // } else if (config?.envs?.[process.env.NODE_ENV || 'dev']?.db?.[0]) { + // } else if (config?.envs?.[process.env.NODE_ENV || 'dev']?.db?.[0]) { } else if (config?.envs?.['_noco']?.db?.[0]) { config.auth = { jwt: { // dbAlias: process.env.NC_AUTH_JWT_DB_ALIAS || config.envs[process.env.NODE_ENV || 'dev'].db[0].meta.dbAlias, - dbAlias: process.env.NC_AUTH_JWT_DB_ALIAS || config.envs['_noco'].db[0].meta.dbAlias, + dbAlias: + process.env.NC_AUTH_JWT_DB_ALIAS || + config.envs['_noco'].db[0].meta.dbAlias, secret: process.env.NC_AUTH_JWT_SECRET } }; } - if (process.env.NC_DB) { - config.meta.db = this.metaUrlToDbConfig(process.env.NC_DB) + config.meta.db = this.metaUrlToDbConfig(process.env.NC_DB); } - if (process.env.NC_TRY) { config.try = true; config.meta.db = { @@ -524,59 +567,56 @@ export default class NcConfigFactory implements NcConfig { } as any; } - if (process.env.NC_MAILER) { config.mailer = { from: process.env.NC_MAILER_FROM, options: { - "host": process.env.NC_MAILER_HOST, - "port": parseInt(process.env.NC_MAILER_PORT, 10), - "secure": process.env.NC_MAILER_SECURE === 'true', - "auth": { - "user": process.env.NC_MAILER_USER, - "pass": process.env.NC_MAILER_PASS + host: process.env.NC_MAILER_HOST, + port: parseInt(process.env.NC_MAILER_PORT, 10), + secure: process.env.NC_MAILER_SECURE === 'true', + auth: { + user: process.env.NC_MAILER_USER, + pass: process.env.NC_MAILER_PASS } } - } + }; } - if (process.env.NC_PUBLIC_URL) { // config.envs[process.env.NODE_ENV || 'dev'].publicUrl = process.env.NC_PUBLIC_URL; config.envs['_noco'].publicUrl = process.env.NC_PUBLIC_URL; config.publicUrl = process.env.NC_PUBLIC_URL; } - config.port = +(process?.env?.PORT ?? 8080); // config.env = process.env?.NODE_ENV || 'dev'; // config.workingEnv = process.env?.NODE_ENV || 'dev'; config.env = '_noco'; config.workingEnv = '_noco'; config.toolDir = process.env.NC_TOOL_DIR || process.cwd(); - config.projectType = type || config?.envs?.[config.workingEnv]?.db?.[0]?.meta?.api?.type || 'rest'; + config.projectType = + type || + config?.envs?.[config.workingEnv]?.db?.[0]?.meta?.api?.type || + 'rest'; return config; } - public static async metaDbCreateIfNotExist(args: NcConfig) { - - if (args.meta?.db?.client === 'sqlite3') { - const metaSqlClient = SqlClientFactory.create( - {...args.meta.db, connection: args.meta.db} - ); - await metaSqlClient.createDatabaseIfNotExists({database: args.meta.db?.connection?.filename}); + const metaSqlClient = SqlClientFactory.create({ + ...args.meta.db, + connection: args.meta.db + }); + await metaSqlClient.createDatabaseIfNotExists({ + database: args.meta.db?.connection?.filename + }); } else { - const metaSqlClient = SqlClientFactory.create( - args.meta.db - ); + const metaSqlClient = SqlClientFactory.create(args.meta.db); await metaSqlClient.createDatabaseIfNotExists(args.meta.db?.connection); await metaSqlClient.knex.destroy(); } - /* const dbPath = path.join(args.toolDir, 'xc.db') const exists = fs.existsSync(dbPath); if (!exists) { @@ -588,10 +628,12 @@ export default class NcConfigFactory implements NcConfig { public version = '0.6'; public port: number; public auth?: AuthConfig; - public env: "production" | "dev" | "test" | string; + public env: 'production' | 'dev' | 'test' | string; public workingEnv: string; public toolDir: string; - public envs: { [p: string]: { db: DbConfig[]; api?: any, publicUrl?: string } }; + public envs: { + [p: string]: { db: DbConfig[]; api?: any; publicUrl?: string }; + }; // public projectType: "rest" | "graphql" | "grpc"; public queriesFolder: string | string[] = ''; public seedsFolder: string | string[]; @@ -599,55 +641,59 @@ export default class NcConfigFactory implements NcConfig { public publicUrl: string; public projectType; public meta = { - "db": { - "client": "sqlite3", - "connection": { - "filename": "noco.db" + db: { + client: 'sqlite3', + connection: { + filename: 'noco.db' } } - } + }; public mailer: MailerConfig; public try = false; - public dashboardPath = '/dashboard' + public dashboardPath = '/dashboard'; constructor() { - this.envs = {_noco: {db: []}}; + this.envs = { _noco: { db: [] } }; } - public static jdbcToXcUrl() { if (process.env.NC_DATABASE_URL_FILE || process.env.DATABASE_URL_FILE) { - const database_url = fs.readFileSync(process.env.NC_DATABASE_URL_FILE || process.env.DATABASE_URL_FILE, 'utf-8'); + const database_url = fs.readFileSync( + process.env.NC_DATABASE_URL_FILE || process.env.DATABASE_URL_FILE, + 'utf-8' + ); process.env.NC_DB = this.extractXcUrlFromJdbc(database_url); } else if (process.env.NC_DATABASE_URL || process.env.DATABASE_URL) { - process.env.NC_DB = this.extractXcUrlFromJdbc(process.env.NC_DATABASE_URL || process.env.DATABASE_URL); + process.env.NC_DB = this.extractXcUrlFromJdbc( + process.env.NC_DATABASE_URL || process.env.DATABASE_URL + ); } } public static extractXcUrlFromJdbc(url: string) { const config = parseDbUrl(url); const port = config.port || defaultClientPortMapping[config.driver]; - const res = `${driverClientMapping[config.driver] || config.driver}://${config.host}${port ? `:${port}` : ''}?p=${config.password}&u=${config.user}&d=${config.database}`; + const res = `${driverClientMapping[config.driver] || config.driver}://${ + config.host + }${port ? `:${port}` : ''}?p=${config.password}&u=${config.user}&d=${ + config.database + }`; if (config.search_path) { return `${res}&search_path=${config.search_path}`; } return res; } - // public static initOneClickDeployment() { // if (process.env.NC_ONE_CLICK) { // const url = NcConfigFactory.extractXcUrlFromJdbc(process.env.DATABASE_URL); // process.env.NC_DB = url; // } // } - } -export { - defaultConnectionConfig -} +export { defaultConnectionConfig }; /** * @copyright Copyright (c) 2021, Xgene Cloud Ltd diff --git a/packages/nocodb/src/lib/utils/NcHelp.ts b/packages/nocodb/src/lib/utils/NcHelp.ts index dce9e0b3f3..cfe5d5848a 100644 --- a/packages/nocodb/src/lib/utils/NcHelp.ts +++ b/packages/nocodb/src/lib/utils/NcHelp.ts @@ -1,24 +1,30 @@ import debug from 'debug'; export default class NcHelp { - - public static async executeOperations(fns: Array<() => Promise>, dbType: string): Promise { + public static async executeOperations( + fns: Array<() => Promise>, + dbType: string + ): Promise { if (dbType === 'oracledb') { for (const fn of fns) { await fn(); } } else { - await Promise.all(fns.map(async f => { - await f(); - })) + await Promise.all( + fns.map(async f => { + await f(); + }) + ); } } - public static enableOrDisableDebugLog(args: { - [key: string]: boolean + [key: string]: boolean; }): void { - const enabledKeys = debug.disable().split(',').filter(v => v.trim()); + const enabledKeys = debug + .disable() + .split(',') + .filter(v => v.trim()); for (const [key, enabled] of Object.entries(args)) { if (enabled) { if (!enabledKeys.includes(key)) { @@ -31,11 +37,11 @@ export default class NcHelp { } } } - - debug.enable(enabledKeys.join(',')) - } -}/** + debug.enable(enabledKeys.join(',')); + } +} +/** * @copyright Copyright (c) 2021, Xgene Cloud Ltd * * @author Naveen MR diff --git a/packages/nocodb/src/lib/utils/mimeTypes.ts b/packages/nocodb/src/lib/utils/mimeTypes.ts index bd64136360..db972ccfd8 100644 --- a/packages/nocodb/src/lib/utils/mimeTypes.ts +++ b/packages/nocodb/src/lib/utils/mimeTypes.ts @@ -1,721 +1,722 @@ export default { - "123": "application/vnd.lotus-1-2-3", - "x3d": "application/vnd.hzn-3d-crossword", - "3gp": "video/3gpp", - "3g2": "video/3gpp2", - "mseq": "application/vnd.mseq", - "pwn": "application/vnd.3m.post-it-notes", - "plb": "application/vnd.3gpp.pic-bw-large", - "psb": "application/vnd.3gpp.pic-bw-small", - "pvb": "application/vnd.3gpp.pic-bw-var", - "tcap": "application/vnd.3gpp2.tcap", - "7z": "application/x-7z-compressed", - "abw": "application/x-abiword", - "ace": "application/x-ace-compressed", - "acc": "application/vnd.americandynamics.acc", - "acu": "application/vnd.acucobol", - "atc": "application/vnd.acucorp", - "adp": "audio/adpcm", - "aab": "application/x-authorware-bin", - "aam": "application/x-authorware-map", - "aas": "application/x-authorware-seg", - "air": "application/vnd.adobe.air-application-installer-package+zip", - "swf": "application/x-shockwave-flash", - "fxp": "application/vnd.adobe.fxp", - "pdf": "application/pdf", - "ppd": "application/vnd.cups-ppd", - "dir": "application/x-director", - "xdp": "application/vnd.adobe.xdp+xml", - "xfdf": "application/vnd.adobe.xfdf", - "aac": "audio/x-aac", - "ahead": "application/vnd.ahead.space", - "azf": "application/vnd.airzip.filesecure.azf", - "azs": "application/vnd.airzip.filesecure.azs", - "azw": "application/vnd.amazon.ebook", - "ami": "application/vnd.amiga.ami", - "/A": "application/andrew-inset", - "apk": "application/vnd.android.package-archive", - "cii": "application/vnd.anser-web-certificate-issue-initiation", - "fti": "application/vnd.anser-web-funds-transfer-initiation", - "atx": "application/vnd.antix.game-component", - "dmg": "application/x-apple-diskimage", - "mpkg": "application/vnd.apple.installer+xml", - "aw": "application/applixware", - "les": "application/vnd.hhe.lesson-player", - "swi": "application/vnd.aristanetworks.swi", - "s": "text/x-asm", - "atomcat": "application/atomcat+xml", - "atomsvc": "application/atomsvc+xml", - "atom, .xml": "application/atom+xml", - "ac": "application/pkix-attr-cert", - "aif": "audio/x-aiff", - "avi": "video/x-msvideo", - "aep": "application/vnd.audiograph", - "dxf": "image/vnd.dxf", - "dwf": "model/vnd.dwf", - "par": "text/plain-bas", - "bcpio": "application/x-bcpio", - "bin": "application/octet-stream", - "bmp": "image/bmp", - "torrent": "application/x-bittorrent", - "cod": "application/vnd.rim.cod", - "mpm": "application/vnd.blueice.multipass", - "bmi": "application/vnd.bmi", - "sh": "application/x-sh", - "btif": "image/prs.btif", - "rep": "application/vnd.businessobjects", - "bz": "application/x-bzip", - "bz2": "application/x-bzip2", - "csh": "application/x-csh", - "c": "text/x-c", - "cdxml": "application/vnd.chemdraw+xml", - "css": "text/css", - "cdx": "chemical/x-cdx", - "cml": "chemical/x-cml", - "csml": "chemical/x-csml", - "cdbcmsg": "application/vnd.contact.cmsg", - "cla": "application/vnd.claymore", - "c4g": "application/vnd.clonk.c4group", - "sub": "image/vnd.dvb.subtitle", - "cdmia": "application/cdmi-capability", - "cdmic": "application/cdmi-container", - "cdmid": "application/cdmi-domain", - "cdmio": "application/cdmi-object", - "cdmiq": "application/cdmi-queue", - "c11amc": "application/vnd.cluetrust.cartomobile-config", - "c11amz": "application/vnd.cluetrust.cartomobile-config-pkg", - "ras": "image/x-cmu-raster", - "dae": "model/vnd.collada+xml", - "csv": "text/csv", - "cpt": "application/mac-compactpro", - "wmlc": "application/vnd.wap.wmlc", - "cgm": "image/cgm", - "ice": "x-conference/x-cooltalk", - "cmx": "image/x-cmx", - "xar": "application/vnd.xara", - "cmc": "application/vnd.cosmocaller", - "cpio": "application/x-cpio", - "clkx": "application/vnd.crick.clicker", - "clkk": "application/vnd.crick.clicker.keyboard", - "clkp": "application/vnd.crick.clicker.palette", - "clkt": "application/vnd.crick.clicker.template", - "clkw": "application/vnd.crick.clicker.wordbank", - "wbs": "application/vnd.criticaltools.wbs+xml", - "cryptonote": "application/vnd.rig.cryptonote", - "cif": "chemical/x-cif", - "cmdf": "chemical/x-cmdf", - "cu": "application/cu-seeme", - "cww": "application/prs.cww", - "curl": "text/vnd.curl", - "dcurl": "text/vnd.curl.dcurl", - "mcurl": "text/vnd.curl.mcurl", - "scurl": "text/vnd.curl.scurl", - "car": "application/vnd.curl.car", - "pcurl": "application/vnd.curl.pcurl", - "cmp": "application/vnd.yellowriver-custom-menu", - "dssc": "application/dssc+der", - "xdssc": "application/dssc+xml", - "deb": "application/x-debian-package", - "uva": "audio/vnd.dece.audio", - "uvi": "image/vnd.dece.graphic", - "uvh": "video/vnd.dece.hd", - "uvm": "video/vnd.dece.mobile", - "uvu": "video/vnd.uvvu.mp4", - "uvp": "video/vnd.dece.pd", - "uvs": "video/vnd.dece.sd", - "uvv": "video/vnd.dece.video", - "dvi": "application/x-dvi", - "seed": "application/vnd.fdsn.seed", - "dtb": "application/x-dtbook+xml", - "res": "application/x-dtbresource+xml", - "ait": "application/vnd.dvb.ait", - "svc": "application/vnd.dvb.service", - "eol": "audio/vnd.digital-winds", - "djvu": "image/vnd.djvu", - "dtd": "application/xml-dtd", - "mlp": "application/vnd.dolby.mlp", - "wad": "application/x-doom", - "dpg": "application/vnd.dpgraph", - "dra": "audio/vnd.dra", - "dfac": "application/vnd.dreamfactory", - "dts": "audio/vnd.dts", - "dtshd": "audio/vnd.dts.hd", - "dwg": "image/vnd.dwg", - "geo": "application/vnd.dynageo", - "es": "application/ecmascript", - "mag": "application/vnd.ecowin.chart", - "mmr": "image/vnd.fujixerox.edmics-mmr", - "rlc": "image/vnd.fujixerox.edmics-rlc", - "exi": "application/exi", - "mgz": "application/vnd.proteus.magazine", - "epub": "application/epub+zip", - "eml": "message/rfc822", - "nml": "application/vnd.enliven", - "xpr": "application/vnd.is-xpr", - "xif": "image/vnd.xiff", - "xfdl": "application/vnd.xfdl", - "emma": "application/emma+xml", - "ez2": "application/vnd.ezpix-album", - "ez3": "application/vnd.ezpix-package", - "fst": "image/vnd.fst", - "fvt": "video/vnd.fvt", - "fbs": "image/vnd.fastbidsheet", - "fe_launch": "application/vnd.denovo.fcselayout-link", - "f4v": "video/x-f4v", - "flv": "video/x-flv", - "fpx": "image/vnd.fpx", - "npx": "image/vnd.net-fpx", - "flx": "text/vnd.fmi.flexstor", - "fli": "video/x-fli", - "ftc": "application/vnd.fluxtime.clip", - "fdf": "application/vnd.fdf", - "f": "text/x-fortran", - "mif": "application/vnd.mif", - "fm": "application/vnd.framemaker", - "fh": "image/x-freehand", - "fsc": "application/vnd.fsc.weblaunch", - "fnc": "application/vnd.frogans.fnc", - "ltf": "application/vnd.frogans.ltf", - "ddd": "application/vnd.fujixerox.ddd", - "xdw": "application/vnd.fujixerox.docuworks", - "xbd": "application/vnd.fujixerox.docuworks.binder", - "oas": "application/vnd.fujitsu.oasys", - "oa2": "application/vnd.fujitsu.oasys2", - "oa3": "application/vnd.fujitsu.oasys3", - "fg5": "application/vnd.fujitsu.oasysgp", - "bh2": "application/vnd.fujitsu.oasysprs", - "spl": "application/x-futuresplash", - "fzs": "application/vnd.fuzzysheet", - "g3": "image/g3fax", - "gmx": "application/vnd.gmx", - "gtw": "model/vnd.gtw", - "txd": "application/vnd.genomatix.tuxedo", - "ggb": "application/vnd.geogebra.file", - "ggt": "application/vnd.geogebra.tool", - "gdl": "model/vnd.gdl", - "gex": "application/vnd.geometry-explorer", - "gxt": "application/vnd.geonext", - "g2w": "application/vnd.geoplan", - "g3w": "application/vnd.geospace", - "gsf": "application/x-font-ghostscript", - "bdf": "application/x-font-bdf", - "gtar": "application/x-gtar", - "texinfo": "application/x-texinfo", - "gnumeric": "application/x-gnumeric", - "kml": "application/vnd.google-earth.kml+xml", - "kmz": "application/vnd.google-earth.kmz", - "gpx": "application/gpx+xml", - "gqf": "application/vnd.grafeq", - "gif": "image/gif", - "gv": "text/vnd.graphviz", - "gac": "application/vnd.groove-account", - "ghf": "application/vnd.groove-help", - "gim": "application/vnd.groove-identity-message", - "grv": "application/vnd.groove-injector", - "gtm": "application/vnd.groove-tool-message", - "tpl": "application/vnd.groove-tool-template", - "vcg": "application/vnd.groove-vcard", - "h261": "video/h261", - "h263": "video/h263", - "h264": "video/h264", - "hpid": "application/vnd.hp-hpid", - "hps": "application/vnd.hp-hps", - "hdf": "application/x-hdf", - "rip": "audio/vnd.rip", - "hbci": "application/vnd.hbci", - "jlt": "application/vnd.hp-jlyt", - "pcl": "application/vnd.hp-pcl", - "hpgl": "application/vnd.hp-hpgl", - "hvs": "application/vnd.yamaha.hv-script", - "hvd": "application/vnd.yamaha.hv-dic", - "hvp": "application/vnd.yamaha.hv-voice", - "sfd-hdstx": "application/vnd.hydrostatix.sof-data", - "stk": "application/hyperstudio", - "hal": "application/vnd.hal+xml", - "html": "text/html", - "irm": "application/vnd.ibm.rights-management", - "sc": "application/vnd.ibm.secure-container", - "ics": "text/calendar", - "icc": "application/vnd.iccprofile", - "ico": "image/x-icon", - "igl": "application/vnd.igloader", - "ief": "image/ief", - "ivp": "application/vnd.immervision-ivp", - "ivu": "application/vnd.immervision-ivu", - "rif": "application/reginfo+xml", - "3dml": "text/vnd.in3d.3dml", - "spot": "text/vnd.in3d.spot", - "igs": "model/iges", - "i2g": "application/vnd.intergeo", - "cdy": "application/vnd.cinderella", - "xpw": "application/vnd.intercon.formnet", - "fcs": "application/vnd.isac.fcs", - "ipfix": "application/ipfix", - "cer": "application/pkix-cert", - "pki": "application/pkixcmp", - "crl": "application/pkix-crl", - "pkipath": "application/pkix-pkipath", - "igm": "application/vnd.insors.igm", - "rcprofile": "application/vnd.ipunplugged.rcprofile", - "irp": "application/vnd.irepository.package+xml", - "jad": "text/vnd.sun.j2me.app-descriptor", - "jar": "application/java-archive", - "class": "application/java-vm", - "jnlp": "application/x-java-jnlp-file", - "ser": "application/java-serialized-object", - "java": "text/x-java-source,java", - "js": "application/javascript", - "json": "application/json", - "joda": "application/vnd.joost.joda-archive", - "jpm": "video/jpm", - "jpeg": "image/x-citrix-jpeg", - "jpg": "image/x-citrix-jpeg", - "pjpeg": "image/pjpeg", - "jpgv": "video/jpeg", - "ktz": "application/vnd.kahootz", - "mmd": "application/vnd.chipnuts.karaoke-mmd", - "karbon": "application/vnd.kde.karbon", - "chrt": "application/vnd.kde.kchart", - "kfo": "application/vnd.kde.kformula", - "flw": "application/vnd.kde.kivio", - "kon": "application/vnd.kde.kontour", - "kpr": "application/vnd.kde.kpresenter", - "ksp": "application/vnd.kde.kspread", - "kwd": "application/vnd.kde.kword", - "htke": "application/vnd.kenameaapp", - "kia": "application/vnd.kidspiration", - "kne": "application/vnd.kinar", - "sse": "application/vnd.kodak-descriptor", - "lasxml": "application/vnd.las.las+xml", - "latex": "application/x-latex", - "lbd": "application/vnd.llamagraphics.life-balance.desktop", - "lbe": "application/vnd.llamagraphics.life-balance.exchange+xml", - "jam": "application/vnd.jam", - "apr": "application/vnd.lotus-approach", - "pre": "application/vnd.lotus-freelance", - "nsf": "application/vnd.lotus-notes", - "org": "application/vnd.lotus-organizer", - "scm": "application/vnd.lotus-screencam", - "lwp": "application/vnd.lotus-wordpro", - "lvp": "audio/vnd.lucent.voice", - "m3u": "audio/x-mpegurl", - "m4v": "video/x-m4v", - "hqx": "application/mac-binhex40", - "portpkg": "application/vnd.macports.portpkg", - "mgp": "application/vnd.osgeo.mapguide.package", - "mrc": "application/marc", - "mrcx": "application/marcxml+xml", - "mxf": "application/mxf", - "nbp": "application/vnd.wolfram.player", - "ma": "application/mathematica", - "mathml": "application/mathml+xml", - "mbox": "application/mbox", - "mc1": "application/vnd.medcalcdata", - "mscml": "application/mediaservercontrol+xml", - "cdkey": "application/vnd.mediastation.cdkey", - "mwf": "application/vnd.mfer", - "mfm": "application/vnd.mfmp", - "msh": "model/mesh", - "mads": "application/mads+xml", - "mets": "application/mets+xml", - "mods": "application/mods+xml", - "meta4": "application/metalink4+xml", - "mcd": "application/vnd.mcd", - "flo": "application/vnd.micrografx.flo", - "igx": "application/vnd.micrografx.igx", - "es3": "application/vnd.eszigno3+xml", - "mdb": "application/x-msaccess", - "asf": "video/x-ms-asf", - "exe": "application/x-msdownload", - "cil": "application/vnd.ms-artgalry", - "cab": "application/vnd.ms-cab-compressed", - "ims": "application/vnd.ms-ims", - "application": "application/x-ms-application", - "clp": "application/x-msclip", - "mdi": "image/vnd.ms-modi", - "eot": "application/vnd.ms-fontobject", - "xls": "application/vnd.ms-excel", - "xlam": "application/vnd.ms-excel.addin.macroenabled.12", - "xlsb": "application/vnd.ms-excel.sheet.binary.macroenabled.12", - "xltm": "application/vnd.ms-excel.template.macroenabled.12", - "xlsm": "application/vnd.ms-excel.sheet.macroenabled.12", - "chm": "application/vnd.ms-htmlhelp", - "crd": "application/x-mscardfile", - "lrm": "application/vnd.ms-lrm", - "mvb": "application/x-msmediaview", - "mny": "application/x-msmoney", - "pptx": "application/vnd.openxmlformats-officedocument.presentationml.presentation", - "sldx": "application/vnd.openxmlformats-officedocument.presentationml.slide", - "ppsx": "application/vnd.openxmlformats-officedocument.presentationml.slideshow", - "potx": "application/vnd.openxmlformats-officedocument.presentationml.template", - "xlsx": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", - "xltx": "application/vnd.openxmlformats-officedocument.spreadsheetml.template", - "docx": "application/vnd.openxmlformats-officedocument.wordprocessingml.document", - "dotx": "application/vnd.openxmlformats-officedocument.wordprocessingml.template", - "obd": "application/x-msbinder", - "thmx": "application/vnd.ms-officetheme", - "onetoc": "application/onenote", - "pya": "audio/vnd.ms-playready.media.pya", - "pyv": "video/vnd.ms-playready.media.pyv", - "ppt": "application/vnd.ms-powerpoint", - "ppam": "application/vnd.ms-powerpoint.addin.macroenabled.12", - "sldm": "application/vnd.ms-powerpoint.slide.macroenabled.12", - "pptm": "application/vnd.ms-powerpoint.presentation.macroenabled.12", - "ppsm": "application/vnd.ms-powerpoint.slideshow.macroenabled.12", - "potm": "application/vnd.ms-powerpoint.template.macroenabled.12", - "mpp": "application/vnd.ms-project", - "pub": "application/x-mspublisher", - "scd": "application/x-msschedule", - "xap": "application/x-silverlight-app", - "stl": "application/vnd.ms-pki.stl", - "cat": "application/vnd.ms-pki.seccat", - "vsd": "application/vnd.visio", - "vsdx": "application/vnd.visio2013", - "wm": "video/x-ms-wm", - "wma": "audio/x-ms-wma", - "wax": "audio/x-ms-wax", - "wmx": "video/x-ms-wmx", - "wmd": "application/x-ms-wmd", - "wpl": "application/vnd.ms-wpl", - "wmz": "application/x-ms-wmz", - "wmv": "video/x-ms-wmv", - "wvx": "video/x-ms-wvx", - "wmf": "application/x-msmetafile", - "trm": "application/x-msterminal", - "doc": "application/msword", - "docm": "application/vnd.ms-word.document.macroenabled.12", - "dotm": "application/vnd.ms-word.template.macroenabled.12", - "wri": "application/x-mswrite", - "wps": "application/vnd.ms-works", - "xbap": "application/x-ms-xbap", - "xps": "application/vnd.ms-xpsdocument", - "mid": "audio/midi", - "mpy": "application/vnd.ibm.minipay", - "afp": "application/vnd.ibm.modcap", - "rms": "application/vnd.jcp.javame.midlet-rms", - "tmo": "application/vnd.tmobile-livetv", - "prc": "application/x-mobipocket-ebook", - "mbk": "application/vnd.mobius.mbk", - "dis": "application/vnd.mobius.dis", - "plc": "application/vnd.mobius.plc", - "mqy": "application/vnd.mobius.mqy", - "msl": "application/vnd.mobius.msl", - "txf": "application/vnd.mobius.txf", - "daf": "application/vnd.mobius.daf", - "fly": "text/vnd.fly", - "mpc": "application/vnd.mophun.certificate", - "mpn": "application/vnd.mophun.application", - "mj2": "video/mj2", - "mpga": "audio/mpeg", - "mxu": "video/vnd.mpegurl", - "mpeg": "video/mpeg", - "m21": "application/mp21", - "mp4a": "audio/mp4", - "mp4": "application/mp4", - "m3u8": "application/vnd.apple.mpegurl", - "mus": "application/vnd.musician", - "msty": "application/vnd.muvee.style", - "mxml": "application/xv+xml", - "ngdat": "application/vnd.nokia.n-gage.data", - "n-gage": "application/vnd.nokia.n-gage.symbian.install", - "ncx": "application/x-dtbncx+xml", - "nc": "application/x-netcdf", - "nlu": "application/vnd.neurolanguage.nlu", - "dna": "application/vnd.dna", - "nnd": "application/vnd.noblenet-directory", - "nns": "application/vnd.noblenet-sealer", - "nnw": "application/vnd.noblenet-web", - "rpst": "application/vnd.nokia.radio-preset", - "rpss": "application/vnd.nokia.radio-presets", - "n3": "text/n3", - "edm": "application/vnd.novadigm.edm", - "edx": "application/vnd.novadigm.edx", - "ext": "application/vnd.novadigm.ext", - "gph": "application/vnd.flographit", - "ecelp4800": "audio/vnd.nuera.ecelp4800", - "ecelp7470": "audio/vnd.nuera.ecelp7470", - "ecelp9600": "audio/vnd.nuera.ecelp9600", - "oda": "application/oda", - "ogx": "application/ogg", - "oga": "audio/ogg", - "ogv": "video/ogg", - "dd2": "application/vnd.oma.dd2+xml", - "oth": "application/vnd.oasis.opendocument.text-web", - "opf": "application/oebps-package+xml", - "qbo": "application/vnd.intu.qbo", - "oxt": "application/vnd.openofficeorg.extension", - "osf": "application/vnd.yamaha.openscoreformat", - "weba": "audio/webm", - "webm": "video/webm", - "odc": "application/vnd.oasis.opendocument.chart", - "otc": "application/vnd.oasis.opendocument.chart-template", - "odb": "application/vnd.oasis.opendocument.database", - "odf": "application/vnd.oasis.opendocument.formula", - "odft": "application/vnd.oasis.opendocument.formula-template", - "odg": "application/vnd.oasis.opendocument.graphics", - "otg": "application/vnd.oasis.opendocument.graphics-template", - "odi": "application/vnd.oasis.opendocument.image", - "oti": "application/vnd.oasis.opendocument.image-template", - "odp": "application/vnd.oasis.opendocument.presentation", - "otp": "application/vnd.oasis.opendocument.presentation-template", - "ods": "application/vnd.oasis.opendocument.spreadsheet", - "ots": "application/vnd.oasis.opendocument.spreadsheet-template", - "odt": "application/vnd.oasis.opendocument.text", - "odm": "application/vnd.oasis.opendocument.text-master", - "ott": "application/vnd.oasis.opendocument.text-template", - "ktx": "image/ktx", - "sxc": "application/vnd.sun.xml.calc", - "stc": "application/vnd.sun.xml.calc.template", - "sxd": "application/vnd.sun.xml.draw", - "std": "application/vnd.sun.xml.draw.template", - "sxi": "application/vnd.sun.xml.impress", - "sti": "application/vnd.sun.xml.impress.template", - "sxm": "application/vnd.sun.xml.math", - "sxw": "application/vnd.sun.xml.writer", - "sxg": "application/vnd.sun.xml.writer.global", - "stw": "application/vnd.sun.xml.writer.template", - "otf": "application/x-font-otf", - "osfpvg": "application/vnd.yamaha.openscoreformat.osfpvg+xml", - "dp": "application/vnd.osgi.dp", - "pdb": "application/vnd.palm", - "p": "text/x-pascal", - "paw": "application/vnd.pawaafile", - "pclxl": "application/vnd.hp-pclxl", - "efif": "application/vnd.picsel", - "pcx": "image/x-pcx", - "psd": "image/vnd.adobe.photoshop", - "prf": "application/pics-rules", - "pic": "image/x-pict", - "chat": "application/x-chat", - "p10": "application/pkcs10", - "p12": "application/x-pkcs12", - "p7m": "application/pkcs7-mime", - "p7s": "application/pkcs7-signature", - "p7r": "application/x-pkcs7-certreqresp", - "p7b": "application/x-pkcs7-certificates", - "p8": "application/pkcs8", - "plf": "application/vnd.pocketlearn", - "pnm": "image/x-portable-anymap", - "pbm": "image/x-portable-bitmap", - "pcf": "application/x-font-pcf", - "pfr": "application/font-tdpfr", - "pgn": "application/x-chess-pgn", - "pgm": "image/x-portable-graymap", - "png": "image/x-png", - "ppm": "image/x-portable-pixmap", - "pskcxml": "application/pskc+xml", - "pml": "application/vnd.ctc-posml", - "ai": "application/postscript", - "pfa": "application/x-font-type1", - "pbd": "application/vnd.powerbuilder6", - "pgp": "application/pgp-signature", - "box": "application/vnd.previewsystems.box", - "ptid": "application/vnd.pvi.ptid1", - "pls": "application/pls+xml", - "str": "application/vnd.pg.format", - "ei6": "application/vnd.pg.osasli", - "dsc": "text/prs.lines.tag", - "psf": "application/x-font-linux-psf", - "qps": "application/vnd.publishare-delta-tree", - "wg": "application/vnd.pmi.widget", - "qxd": "application/vnd.quark.quarkxpress", - "esf": "application/vnd.epson.esf", - "msf": "application/vnd.epson.msf", - "ssf": "application/vnd.epson.ssf", - "qam": "application/vnd.epson.quickanime", - "qfx": "application/vnd.intu.qfx", - "qt": "video/quicktime", - "rar": "application/x-rar-compressed", - "ram": "audio/x-pn-realaudio", - "rmp": "audio/x-pn-realaudio-plugin", - "rsd": "application/rsd+xml", - "rm": "application/vnd.rn-realmedia", - "bed": "application/vnd.realvnc.bed", - "mxl": "application/vnd.recordare.musicxml", - "musicxml": "application/vnd.recordare.musicxml+xml", - "rnc": "application/relax-ng-compact-syntax", - "rdz": "application/vnd.data-vision.rdz", - "rdf": "application/rdf+xml", - "rp9": "application/vnd.cloanto.rp9", - "jisp": "application/vnd.jisp", - "rtf": "application/rtf", - "rtx": "text/richtext", - "link66": "application/vnd.route66.link66+xml", - "rss, .xml": "application/rss+xml", - "shf": "application/shf+xml", - "st": "application/vnd.sailingtracker.track", - "svg": "image/svg+xml", - "sus": "application/vnd.sus-calendar", - "sru": "application/sru+xml", - "setpay": "application/set-payment-initiation", - "setreg": "application/set-registration-initiation", - "sema": "application/vnd.sema", - "semd": "application/vnd.semd", - "semf": "application/vnd.semf", - "see": "application/vnd.seemail", - "snf": "application/x-font-snf", - "spq": "application/scvp-vp-request", - "spp": "application/scvp-vp-response", - "scq": "application/scvp-cv-request", - "scs": "application/scvp-cv-response", - "sdp": "application/sdp", - "etx": "text/x-setext", - "movie": "video/x-sgi-movie", - "ifm": "application/vnd.shana.informed.formdata", - "itp": "application/vnd.shana.informed.formtemplate", - "iif": "application/vnd.shana.informed.interchange", - "ipk": "application/vnd.shana.informed.package", - "tfi": "application/thraud+xml", - "shar": "application/x-shar", - "rgb": "image/x-rgb", - "slt": "application/vnd.epson.salt", - "aso": "application/vnd.accpac.simply.aso", - "imp": "application/vnd.accpac.simply.imp", - "twd": "application/vnd.simtech-mindmapper", - "csp": "application/vnd.commonspace", - "saf": "application/vnd.yamaha.smaf-audio", - "mmf": "application/vnd.smaf", - "spf": "application/vnd.yamaha.smaf-phrase", - "teacher": "application/vnd.smart.teacher", - "svd": "application/vnd.svd", - "rq": "application/sparql-query", - "srx": "application/sparql-results+xml", - "gram": "application/srgs", - "grxml": "application/srgs+xml", - "ssml": "application/ssml+xml", - "skp": "application/vnd.koan", - "sgml": "text/sgml", - "sdc": "application/vnd.stardivision.calc", - "sda": "application/vnd.stardivision.draw", - "sdd": "application/vnd.stardivision.impress", - "smf": "application/vnd.stardivision.math", - "sdw": "application/vnd.stardivision.writer", - "sgl": "application/vnd.stardivision.writer-global", - "sm": "application/vnd.stepmania.stepchart", - "sit": "application/x-stuffit", - "sitx": "application/x-stuffitx", - "sdkm": "application/vnd.solent.sdkm+xml", - "xo": "application/vnd.olpc-sugar", - "au": "audio/basic", - "wqd": "application/vnd.wqd", - "sis": "application/vnd.symbian.install", - "smi": "application/smil+xml", - "xsm": "application/vnd.syncml+xml", - "bdm": "application/vnd.syncml.dm+wbxml", - "xdm": "application/vnd.syncml.dm+xml", - "sv4cpio": "application/x-sv4cpio", - "sv4crc": "application/x-sv4crc", - "sbml": "application/sbml+xml", - "tsv": "text/tab-separated-values", - "tiff": "image/tiff", - "tao": "application/vnd.tao.intent-module-archive", - "tar": "application/x-tar", - "tcl": "application/x-tcl", - "tex": "application/x-tex", - "tfm": "application/x-tex-tfm", - "tei": "application/tei+xml", - "txt": "text/plain", - "dxp": "application/vnd.spotfire.dxp", - "sfs": "application/vnd.spotfire.sfs", - "tsd": "application/timestamped-data", - "tpt": "application/vnd.trid.tpt", - "mxs": "application/vnd.triscape.mxs", - "t": "text/troff", - "tra": "application/vnd.trueapp", - "ttf": "application/x-font-ttf", - "ttl": "text/turtle", - "umj": "application/vnd.umajin", - "uoml": "application/vnd.uoml+xml", - "unityweb": "application/vnd.unity", - "ufd": "application/vnd.ufdl", - "uri": "text/uri-list", - "utz": "application/vnd.uiq.theme", - "ustar": "application/x-ustar", - "uu": "text/x-uuencode", - "vcs": "text/x-vcalendar", - "vcf": "text/x-vcard", - "vcd": "application/x-cdlink", - "vsf": "application/vnd.vsf", - "wrl": "model/vrml", - "vcx": "application/vnd.vcx", - "mts": "model/vnd.mts", - "vtu": "model/vnd.vtu", - "vis": "application/vnd.visionary", - "viv": "video/vnd.vivo", - "ccxml": "application/ccxml+xml,", - "vxml": "application/voicexml+xml", - "src": "application/x-wais-source", - "wbxml": "application/vnd.wap.wbxml", - "wbmp": "image/vnd.wap.wbmp", - "wav": "audio/x-wav", - "davmount": "application/davmount+xml", - "woff": "application/x-font-woff", - "wspolicy": "application/wspolicy+xml", - "webp": "image/webp", - "wtb": "application/vnd.webturbo", - "wgt": "application/widget", - "hlp": "application/winhlp", - "wml": "text/vnd.wap.wml", - "wmls": "text/vnd.wap.wmlscript", - "wmlsc": "application/vnd.wap.wmlscriptc", - "wpd": "application/vnd.wordperfect", - "stf": "application/vnd.wt.stf", - "wsdl": "application/wsdl+xml", - "xbm": "image/x-xbitmap", - "xpm": "image/x-xpixmap", - "xwd": "image/x-xwindowdump", - "der": "application/x-x509-ca-cert", - "fig": "application/x-xfig", - "xhtml": "application/xhtml+xml", - "xml": "application/xml", - "xdf": "application/xcap-diff+xml", - "xenc": "application/xenc+xml", - "xer": "application/patch-ops-error+xml", - "rl": "application/resource-lists+xml", - "rs": "application/rls-services+xml", - "rld": "application/resource-lists-diff+xml", - "xslt": "application/xslt+xml", - "xop": "application/xop+xml", - "xpi": "application/x-xpinstall", - "xspf": "application/xspf+xml", - "xul": "application/vnd.mozilla.xul+xml", - "xyz": "chemical/x-xyz", - "yaml": "text/yaml", - "yang": "application/yang", - "yin": "application/yin+xml", - "zir": "application/vnd.zul", - "zip": "application/zip", - "zmm": "application/vnd.handheld-entertainment+xml", - "zaz": "application/vnd.zzazz.deck+xml" -} + '123': 'application/vnd.lotus-1-2-3', + x3d: 'application/vnd.hzn-3d-crossword', + '3gp': 'video/3gpp', + '3g2': 'video/3gpp2', + mseq: 'application/vnd.mseq', + pwn: 'application/vnd.3m.post-it-notes', + plb: 'application/vnd.3gpp.pic-bw-large', + psb: 'application/vnd.3gpp.pic-bw-small', + pvb: 'application/vnd.3gpp.pic-bw-var', + tcap: 'application/vnd.3gpp2.tcap', + '7z': 'application/x-7z-compressed', + abw: 'application/x-abiword', + ace: 'application/x-ace-compressed', + acc: 'application/vnd.americandynamics.acc', + acu: 'application/vnd.acucobol', + atc: 'application/vnd.acucorp', + adp: 'audio/adpcm', + aab: 'application/x-authorware-bin', + aam: 'application/x-authorware-map', + aas: 'application/x-authorware-seg', + air: 'application/vnd.adobe.air-application-installer-package+zip', + swf: 'application/x-shockwave-flash', + fxp: 'application/vnd.adobe.fxp', + pdf: 'application/pdf', + ppd: 'application/vnd.cups-ppd', + dir: 'application/x-director', + xdp: 'application/vnd.adobe.xdp+xml', + xfdf: 'application/vnd.adobe.xfdf', + aac: 'audio/x-aac', + ahead: 'application/vnd.ahead.space', + azf: 'application/vnd.airzip.filesecure.azf', + azs: 'application/vnd.airzip.filesecure.azs', + azw: 'application/vnd.amazon.ebook', + ami: 'application/vnd.amiga.ami', + '/A': 'application/andrew-inset', + apk: 'application/vnd.android.package-archive', + cii: 'application/vnd.anser-web-certificate-issue-initiation', + fti: 'application/vnd.anser-web-funds-transfer-initiation', + atx: 'application/vnd.antix.game-component', + dmg: 'application/x-apple-diskimage', + mpkg: 'application/vnd.apple.installer+xml', + aw: 'application/applixware', + les: 'application/vnd.hhe.lesson-player', + swi: 'application/vnd.aristanetworks.swi', + s: 'text/x-asm', + atomcat: 'application/atomcat+xml', + atomsvc: 'application/atomsvc+xml', + 'atom, .xml': 'application/atom+xml', + ac: 'application/pkix-attr-cert', + aif: 'audio/x-aiff', + avi: 'video/x-msvideo', + aep: 'application/vnd.audiograph', + dxf: 'image/vnd.dxf', + dwf: 'model/vnd.dwf', + par: 'text/plain-bas', + bcpio: 'application/x-bcpio', + bin: 'application/octet-stream', + bmp: 'image/bmp', + torrent: 'application/x-bittorrent', + cod: 'application/vnd.rim.cod', + mpm: 'application/vnd.blueice.multipass', + bmi: 'application/vnd.bmi', + sh: 'application/x-sh', + btif: 'image/prs.btif', + rep: 'application/vnd.businessobjects', + bz: 'application/x-bzip', + bz2: 'application/x-bzip2', + csh: 'application/x-csh', + c: 'text/x-c', + cdxml: 'application/vnd.chemdraw+xml', + css: 'text/css', + cdx: 'chemical/x-cdx', + cml: 'chemical/x-cml', + csml: 'chemical/x-csml', + cdbcmsg: 'application/vnd.contact.cmsg', + cla: 'application/vnd.claymore', + c4g: 'application/vnd.clonk.c4group', + sub: 'image/vnd.dvb.subtitle', + cdmia: 'application/cdmi-capability', + cdmic: 'application/cdmi-container', + cdmid: 'application/cdmi-domain', + cdmio: 'application/cdmi-object', + cdmiq: 'application/cdmi-queue', + c11amc: 'application/vnd.cluetrust.cartomobile-config', + c11amz: 'application/vnd.cluetrust.cartomobile-config-pkg', + ras: 'image/x-cmu-raster', + dae: 'model/vnd.collada+xml', + csv: 'text/csv', + cpt: 'application/mac-compactpro', + wmlc: 'application/vnd.wap.wmlc', + cgm: 'image/cgm', + ice: 'x-conference/x-cooltalk', + cmx: 'image/x-cmx', + xar: 'application/vnd.xara', + cmc: 'application/vnd.cosmocaller', + cpio: 'application/x-cpio', + clkx: 'application/vnd.crick.clicker', + clkk: 'application/vnd.crick.clicker.keyboard', + clkp: 'application/vnd.crick.clicker.palette', + clkt: 'application/vnd.crick.clicker.template', + clkw: 'application/vnd.crick.clicker.wordbank', + wbs: 'application/vnd.criticaltools.wbs+xml', + cryptonote: 'application/vnd.rig.cryptonote', + cif: 'chemical/x-cif', + cmdf: 'chemical/x-cmdf', + cu: 'application/cu-seeme', + cww: 'application/prs.cww', + curl: 'text/vnd.curl', + dcurl: 'text/vnd.curl.dcurl', + mcurl: 'text/vnd.curl.mcurl', + scurl: 'text/vnd.curl.scurl', + car: 'application/vnd.curl.car', + pcurl: 'application/vnd.curl.pcurl', + cmp: 'application/vnd.yellowriver-custom-menu', + dssc: 'application/dssc+der', + xdssc: 'application/dssc+xml', + deb: 'application/x-debian-package', + uva: 'audio/vnd.dece.audio', + uvi: 'image/vnd.dece.graphic', + uvh: 'video/vnd.dece.hd', + uvm: 'video/vnd.dece.mobile', + uvu: 'video/vnd.uvvu.mp4', + uvp: 'video/vnd.dece.pd', + uvs: 'video/vnd.dece.sd', + uvv: 'video/vnd.dece.video', + dvi: 'application/x-dvi', + seed: 'application/vnd.fdsn.seed', + dtb: 'application/x-dtbook+xml', + res: 'application/x-dtbresource+xml', + ait: 'application/vnd.dvb.ait', + svc: 'application/vnd.dvb.service', + eol: 'audio/vnd.digital-winds', + djvu: 'image/vnd.djvu', + dtd: 'application/xml-dtd', + mlp: 'application/vnd.dolby.mlp', + wad: 'application/x-doom', + dpg: 'application/vnd.dpgraph', + dra: 'audio/vnd.dra', + dfac: 'application/vnd.dreamfactory', + dts: 'audio/vnd.dts', + dtshd: 'audio/vnd.dts.hd', + dwg: 'image/vnd.dwg', + geo: 'application/vnd.dynageo', + es: 'application/ecmascript', + mag: 'application/vnd.ecowin.chart', + mmr: 'image/vnd.fujixerox.edmics-mmr', + rlc: 'image/vnd.fujixerox.edmics-rlc', + exi: 'application/exi', + mgz: 'application/vnd.proteus.magazine', + epub: 'application/epub+zip', + eml: 'message/rfc822', + nml: 'application/vnd.enliven', + xpr: 'application/vnd.is-xpr', + xif: 'image/vnd.xiff', + xfdl: 'application/vnd.xfdl', + emma: 'application/emma+xml', + ez2: 'application/vnd.ezpix-album', + ez3: 'application/vnd.ezpix-package', + fst: 'image/vnd.fst', + fvt: 'video/vnd.fvt', + fbs: 'image/vnd.fastbidsheet', + fe_launch: 'application/vnd.denovo.fcselayout-link', + f4v: 'video/x-f4v', + flv: 'video/x-flv', + fpx: 'image/vnd.fpx', + npx: 'image/vnd.net-fpx', + flx: 'text/vnd.fmi.flexstor', + fli: 'video/x-fli', + ftc: 'application/vnd.fluxtime.clip', + fdf: 'application/vnd.fdf', + f: 'text/x-fortran', + mif: 'application/vnd.mif', + fm: 'application/vnd.framemaker', + fh: 'image/x-freehand', + fsc: 'application/vnd.fsc.weblaunch', + fnc: 'application/vnd.frogans.fnc', + ltf: 'application/vnd.frogans.ltf', + ddd: 'application/vnd.fujixerox.ddd', + xdw: 'application/vnd.fujixerox.docuworks', + xbd: 'application/vnd.fujixerox.docuworks.binder', + oas: 'application/vnd.fujitsu.oasys', + oa2: 'application/vnd.fujitsu.oasys2', + oa3: 'application/vnd.fujitsu.oasys3', + fg5: 'application/vnd.fujitsu.oasysgp', + bh2: 'application/vnd.fujitsu.oasysprs', + spl: 'application/x-futuresplash', + fzs: 'application/vnd.fuzzysheet', + g3: 'image/g3fax', + gmx: 'application/vnd.gmx', + gtw: 'model/vnd.gtw', + txd: 'application/vnd.genomatix.tuxedo', + ggb: 'application/vnd.geogebra.file', + ggt: 'application/vnd.geogebra.tool', + gdl: 'model/vnd.gdl', + gex: 'application/vnd.geometry-explorer', + gxt: 'application/vnd.geonext', + g2w: 'application/vnd.geoplan', + g3w: 'application/vnd.geospace', + gsf: 'application/x-font-ghostscript', + bdf: 'application/x-font-bdf', + gtar: 'application/x-gtar', + texinfo: 'application/x-texinfo', + gnumeric: 'application/x-gnumeric', + kml: 'application/vnd.google-earth.kml+xml', + kmz: 'application/vnd.google-earth.kmz', + gpx: 'application/gpx+xml', + gqf: 'application/vnd.grafeq', + gif: 'image/gif', + gv: 'text/vnd.graphviz', + gac: 'application/vnd.groove-account', + ghf: 'application/vnd.groove-help', + gim: 'application/vnd.groove-identity-message', + grv: 'application/vnd.groove-injector', + gtm: 'application/vnd.groove-tool-message', + tpl: 'application/vnd.groove-tool-template', + vcg: 'application/vnd.groove-vcard', + h261: 'video/h261', + h263: 'video/h263', + h264: 'video/h264', + hpid: 'application/vnd.hp-hpid', + hps: 'application/vnd.hp-hps', + hdf: 'application/x-hdf', + rip: 'audio/vnd.rip', + hbci: 'application/vnd.hbci', + jlt: 'application/vnd.hp-jlyt', + pcl: 'application/vnd.hp-pcl', + hpgl: 'application/vnd.hp-hpgl', + hvs: 'application/vnd.yamaha.hv-script', + hvd: 'application/vnd.yamaha.hv-dic', + hvp: 'application/vnd.yamaha.hv-voice', + 'sfd-hdstx': 'application/vnd.hydrostatix.sof-data', + stk: 'application/hyperstudio', + hal: 'application/vnd.hal+xml', + html: 'text/html', + irm: 'application/vnd.ibm.rights-management', + sc: 'application/vnd.ibm.secure-container', + ics: 'text/calendar', + icc: 'application/vnd.iccprofile', + ico: 'image/x-icon', + igl: 'application/vnd.igloader', + ief: 'image/ief', + ivp: 'application/vnd.immervision-ivp', + ivu: 'application/vnd.immervision-ivu', + rif: 'application/reginfo+xml', + '3dml': 'text/vnd.in3d.3dml', + spot: 'text/vnd.in3d.spot', + igs: 'model/iges', + i2g: 'application/vnd.intergeo', + cdy: 'application/vnd.cinderella', + xpw: 'application/vnd.intercon.formnet', + fcs: 'application/vnd.isac.fcs', + ipfix: 'application/ipfix', + cer: 'application/pkix-cert', + pki: 'application/pkixcmp', + crl: 'application/pkix-crl', + pkipath: 'application/pkix-pkipath', + igm: 'application/vnd.insors.igm', + rcprofile: 'application/vnd.ipunplugged.rcprofile', + irp: 'application/vnd.irepository.package+xml', + jad: 'text/vnd.sun.j2me.app-descriptor', + jar: 'application/java-archive', + class: 'application/java-vm', + jnlp: 'application/x-java-jnlp-file', + ser: 'application/java-serialized-object', + java: 'text/x-java-source,java', + js: 'application/javascript', + json: 'application/json', + joda: 'application/vnd.joost.joda-archive', + jpm: 'video/jpm', + jpeg: 'image/x-citrix-jpeg', + jpg: 'image/x-citrix-jpeg', + pjpeg: 'image/pjpeg', + jpgv: 'video/jpeg', + ktz: 'application/vnd.kahootz', + mmd: 'application/vnd.chipnuts.karaoke-mmd', + karbon: 'application/vnd.kde.karbon', + chrt: 'application/vnd.kde.kchart', + kfo: 'application/vnd.kde.kformula', + flw: 'application/vnd.kde.kivio', + kon: 'application/vnd.kde.kontour', + kpr: 'application/vnd.kde.kpresenter', + ksp: 'application/vnd.kde.kspread', + kwd: 'application/vnd.kde.kword', + htke: 'application/vnd.kenameaapp', + kia: 'application/vnd.kidspiration', + kne: 'application/vnd.kinar', + sse: 'application/vnd.kodak-descriptor', + lasxml: 'application/vnd.las.las+xml', + latex: 'application/x-latex', + lbd: 'application/vnd.llamagraphics.life-balance.desktop', + lbe: 'application/vnd.llamagraphics.life-balance.exchange+xml', + jam: 'application/vnd.jam', + apr: 'application/vnd.lotus-approach', + pre: 'application/vnd.lotus-freelance', + nsf: 'application/vnd.lotus-notes', + org: 'application/vnd.lotus-organizer', + scm: 'application/vnd.lotus-screencam', + lwp: 'application/vnd.lotus-wordpro', + lvp: 'audio/vnd.lucent.voice', + m3u: 'audio/x-mpegurl', + m4v: 'video/x-m4v', + hqx: 'application/mac-binhex40', + portpkg: 'application/vnd.macports.portpkg', + mgp: 'application/vnd.osgeo.mapguide.package', + mrc: 'application/marc', + mrcx: 'application/marcxml+xml', + mxf: 'application/mxf', + nbp: 'application/vnd.wolfram.player', + ma: 'application/mathematica', + mathml: 'application/mathml+xml', + mbox: 'application/mbox', + mc1: 'application/vnd.medcalcdata', + mscml: 'application/mediaservercontrol+xml', + cdkey: 'application/vnd.mediastation.cdkey', + mwf: 'application/vnd.mfer', + mfm: 'application/vnd.mfmp', + msh: 'model/mesh', + mads: 'application/mads+xml', + mets: 'application/mets+xml', + mods: 'application/mods+xml', + meta4: 'application/metalink4+xml', + mcd: 'application/vnd.mcd', + flo: 'application/vnd.micrografx.flo', + igx: 'application/vnd.micrografx.igx', + es3: 'application/vnd.eszigno3+xml', + mdb: 'application/x-msaccess', + asf: 'video/x-ms-asf', + exe: 'application/x-msdownload', + cil: 'application/vnd.ms-artgalry', + cab: 'application/vnd.ms-cab-compressed', + ims: 'application/vnd.ms-ims', + application: 'application/x-ms-application', + clp: 'application/x-msclip', + mdi: 'image/vnd.ms-modi', + eot: 'application/vnd.ms-fontobject', + xls: 'application/vnd.ms-excel', + xlam: 'application/vnd.ms-excel.addin.macroenabled.12', + xlsb: 'application/vnd.ms-excel.sheet.binary.macroenabled.12', + xltm: 'application/vnd.ms-excel.template.macroenabled.12', + xlsm: 'application/vnd.ms-excel.sheet.macroenabled.12', + chm: 'application/vnd.ms-htmlhelp', + crd: 'application/x-mscardfile', + lrm: 'application/vnd.ms-lrm', + mvb: 'application/x-msmediaview', + mny: 'application/x-msmoney', + pptx: + 'application/vnd.openxmlformats-officedocument.presentationml.presentation', + sldx: 'application/vnd.openxmlformats-officedocument.presentationml.slide', + ppsx: + 'application/vnd.openxmlformats-officedocument.presentationml.slideshow', + potx: 'application/vnd.openxmlformats-officedocument.presentationml.template', + xlsx: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', + xltx: 'application/vnd.openxmlformats-officedocument.spreadsheetml.template', + docx: + 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', + dotx: + 'application/vnd.openxmlformats-officedocument.wordprocessingml.template', + obd: 'application/x-msbinder', + thmx: 'application/vnd.ms-officetheme', + onetoc: 'application/onenote', + pya: 'audio/vnd.ms-playready.media.pya', + pyv: 'video/vnd.ms-playready.media.pyv', + ppt: 'application/vnd.ms-powerpoint', + ppam: 'application/vnd.ms-powerpoint.addin.macroenabled.12', + sldm: 'application/vnd.ms-powerpoint.slide.macroenabled.12', + pptm: 'application/vnd.ms-powerpoint.presentation.macroenabled.12', + ppsm: 'application/vnd.ms-powerpoint.slideshow.macroenabled.12', + potm: 'application/vnd.ms-powerpoint.template.macroenabled.12', + mpp: 'application/vnd.ms-project', + pub: 'application/x-mspublisher', + scd: 'application/x-msschedule', + xap: 'application/x-silverlight-app', + stl: 'application/vnd.ms-pki.stl', + cat: 'application/vnd.ms-pki.seccat', + vsd: 'application/vnd.visio', + vsdx: 'application/vnd.visio2013', + wm: 'video/x-ms-wm', + wma: 'audio/x-ms-wma', + wax: 'audio/x-ms-wax', + wmx: 'video/x-ms-wmx', + wmd: 'application/x-ms-wmd', + wpl: 'application/vnd.ms-wpl', + wmz: 'application/x-ms-wmz', + wmv: 'video/x-ms-wmv', + wvx: 'video/x-ms-wvx', + wmf: 'application/x-msmetafile', + trm: 'application/x-msterminal', + doc: 'application/msword', + docm: 'application/vnd.ms-word.document.macroenabled.12', + dotm: 'application/vnd.ms-word.template.macroenabled.12', + wri: 'application/x-mswrite', + wps: 'application/vnd.ms-works', + xbap: 'application/x-ms-xbap', + xps: 'application/vnd.ms-xpsdocument', + mid: 'audio/midi', + mpy: 'application/vnd.ibm.minipay', + afp: 'application/vnd.ibm.modcap', + rms: 'application/vnd.jcp.javame.midlet-rms', + tmo: 'application/vnd.tmobile-livetv', + prc: 'application/x-mobipocket-ebook', + mbk: 'application/vnd.mobius.mbk', + dis: 'application/vnd.mobius.dis', + plc: 'application/vnd.mobius.plc', + mqy: 'application/vnd.mobius.mqy', + msl: 'application/vnd.mobius.msl', + txf: 'application/vnd.mobius.txf', + daf: 'application/vnd.mobius.daf', + fly: 'text/vnd.fly', + mpc: 'application/vnd.mophun.certificate', + mpn: 'application/vnd.mophun.application', + mj2: 'video/mj2', + mpga: 'audio/mpeg', + mxu: 'video/vnd.mpegurl', + mpeg: 'video/mpeg', + m21: 'application/mp21', + mp4a: 'audio/mp4', + mp4: 'application/mp4', + m3u8: 'application/vnd.apple.mpegurl', + mus: 'application/vnd.musician', + msty: 'application/vnd.muvee.style', + mxml: 'application/xv+xml', + ngdat: 'application/vnd.nokia.n-gage.data', + 'n-gage': 'application/vnd.nokia.n-gage.symbian.install', + ncx: 'application/x-dtbncx+xml', + nc: 'application/x-netcdf', + nlu: 'application/vnd.neurolanguage.nlu', + dna: 'application/vnd.dna', + nnd: 'application/vnd.noblenet-directory', + nns: 'application/vnd.noblenet-sealer', + nnw: 'application/vnd.noblenet-web', + rpst: 'application/vnd.nokia.radio-preset', + rpss: 'application/vnd.nokia.radio-presets', + n3: 'text/n3', + edm: 'application/vnd.novadigm.edm', + edx: 'application/vnd.novadigm.edx', + ext: 'application/vnd.novadigm.ext', + gph: 'application/vnd.flographit', + ecelp4800: 'audio/vnd.nuera.ecelp4800', + ecelp7470: 'audio/vnd.nuera.ecelp7470', + ecelp9600: 'audio/vnd.nuera.ecelp9600', + oda: 'application/oda', + ogx: 'application/ogg', + oga: 'audio/ogg', + ogv: 'video/ogg', + dd2: 'application/vnd.oma.dd2+xml', + oth: 'application/vnd.oasis.opendocument.text-web', + opf: 'application/oebps-package+xml', + qbo: 'application/vnd.intu.qbo', + oxt: 'application/vnd.openofficeorg.extension', + osf: 'application/vnd.yamaha.openscoreformat', + weba: 'audio/webm', + webm: 'video/webm', + odc: 'application/vnd.oasis.opendocument.chart', + otc: 'application/vnd.oasis.opendocument.chart-template', + odb: 'application/vnd.oasis.opendocument.database', + odf: 'application/vnd.oasis.opendocument.formula', + odft: 'application/vnd.oasis.opendocument.formula-template', + odg: 'application/vnd.oasis.opendocument.graphics', + otg: 'application/vnd.oasis.opendocument.graphics-template', + odi: 'application/vnd.oasis.opendocument.image', + oti: 'application/vnd.oasis.opendocument.image-template', + odp: 'application/vnd.oasis.opendocument.presentation', + otp: 'application/vnd.oasis.opendocument.presentation-template', + ods: 'application/vnd.oasis.opendocument.spreadsheet', + ots: 'application/vnd.oasis.opendocument.spreadsheet-template', + odt: 'application/vnd.oasis.opendocument.text', + odm: 'application/vnd.oasis.opendocument.text-master', + ott: 'application/vnd.oasis.opendocument.text-template', + ktx: 'image/ktx', + sxc: 'application/vnd.sun.xml.calc', + stc: 'application/vnd.sun.xml.calc.template', + sxd: 'application/vnd.sun.xml.draw', + std: 'application/vnd.sun.xml.draw.template', + sxi: 'application/vnd.sun.xml.impress', + sti: 'application/vnd.sun.xml.impress.template', + sxm: 'application/vnd.sun.xml.math', + sxw: 'application/vnd.sun.xml.writer', + sxg: 'application/vnd.sun.xml.writer.global', + stw: 'application/vnd.sun.xml.writer.template', + otf: 'application/x-font-otf', + osfpvg: 'application/vnd.yamaha.openscoreformat.osfpvg+xml', + dp: 'application/vnd.osgi.dp', + pdb: 'application/vnd.palm', + p: 'text/x-pascal', + paw: 'application/vnd.pawaafile', + pclxl: 'application/vnd.hp-pclxl', + efif: 'application/vnd.picsel', + pcx: 'image/x-pcx', + psd: 'image/vnd.adobe.photoshop', + prf: 'application/pics-rules', + pic: 'image/x-pict', + chat: 'application/x-chat', + p10: 'application/pkcs10', + p12: 'application/x-pkcs12', + p7m: 'application/pkcs7-mime', + p7s: 'application/pkcs7-signature', + p7r: 'application/x-pkcs7-certreqresp', + p7b: 'application/x-pkcs7-certificates', + p8: 'application/pkcs8', + plf: 'application/vnd.pocketlearn', + pnm: 'image/x-portable-anymap', + pbm: 'image/x-portable-bitmap', + pcf: 'application/x-font-pcf', + pfr: 'application/font-tdpfr', + pgn: 'application/x-chess-pgn', + pgm: 'image/x-portable-graymap', + png: 'image/x-png', + ppm: 'image/x-portable-pixmap', + pskcxml: 'application/pskc+xml', + pml: 'application/vnd.ctc-posml', + ai: 'application/postscript', + pfa: 'application/x-font-type1', + pbd: 'application/vnd.powerbuilder6', + pgp: 'application/pgp-signature', + box: 'application/vnd.previewsystems.box', + ptid: 'application/vnd.pvi.ptid1', + pls: 'application/pls+xml', + str: 'application/vnd.pg.format', + ei6: 'application/vnd.pg.osasli', + dsc: 'text/prs.lines.tag', + psf: 'application/x-font-linux-psf', + qps: 'application/vnd.publishare-delta-tree', + wg: 'application/vnd.pmi.widget', + qxd: 'application/vnd.quark.quarkxpress', + esf: 'application/vnd.epson.esf', + msf: 'application/vnd.epson.msf', + ssf: 'application/vnd.epson.ssf', + qam: 'application/vnd.epson.quickanime', + qfx: 'application/vnd.intu.qfx', + qt: 'video/quicktime', + rar: 'application/x-rar-compressed', + ram: 'audio/x-pn-realaudio', + rmp: 'audio/x-pn-realaudio-plugin', + rsd: 'application/rsd+xml', + rm: 'application/vnd.rn-realmedia', + bed: 'application/vnd.realvnc.bed', + mxl: 'application/vnd.recordare.musicxml', + musicxml: 'application/vnd.recordare.musicxml+xml', + rnc: 'application/relax-ng-compact-syntax', + rdz: 'application/vnd.data-vision.rdz', + rdf: 'application/rdf+xml', + rp9: 'application/vnd.cloanto.rp9', + jisp: 'application/vnd.jisp', + rtf: 'application/rtf', + rtx: 'text/richtext', + link66: 'application/vnd.route66.link66+xml', + 'rss, .xml': 'application/rss+xml', + shf: 'application/shf+xml', + st: 'application/vnd.sailingtracker.track', + svg: 'image/svg+xml', + sus: 'application/vnd.sus-calendar', + sru: 'application/sru+xml', + setpay: 'application/set-payment-initiation', + setreg: 'application/set-registration-initiation', + sema: 'application/vnd.sema', + semd: 'application/vnd.semd', + semf: 'application/vnd.semf', + see: 'application/vnd.seemail', + snf: 'application/x-font-snf', + spq: 'application/scvp-vp-request', + spp: 'application/scvp-vp-response', + scq: 'application/scvp-cv-request', + scs: 'application/scvp-cv-response', + sdp: 'application/sdp', + etx: 'text/x-setext', + movie: 'video/x-sgi-movie', + ifm: 'application/vnd.shana.informed.formdata', + itp: 'application/vnd.shana.informed.formtemplate', + iif: 'application/vnd.shana.informed.interchange', + ipk: 'application/vnd.shana.informed.package', + tfi: 'application/thraud+xml', + shar: 'application/x-shar', + rgb: 'image/x-rgb', + slt: 'application/vnd.epson.salt', + aso: 'application/vnd.accpac.simply.aso', + imp: 'application/vnd.accpac.simply.imp', + twd: 'application/vnd.simtech-mindmapper', + csp: 'application/vnd.commonspace', + saf: 'application/vnd.yamaha.smaf-audio', + mmf: 'application/vnd.smaf', + spf: 'application/vnd.yamaha.smaf-phrase', + teacher: 'application/vnd.smart.teacher', + svd: 'application/vnd.svd', + rq: 'application/sparql-query', + srx: 'application/sparql-results+xml', + gram: 'application/srgs', + grxml: 'application/srgs+xml', + ssml: 'application/ssml+xml', + skp: 'application/vnd.koan', + sgml: 'text/sgml', + sdc: 'application/vnd.stardivision.calc', + sda: 'application/vnd.stardivision.draw', + sdd: 'application/vnd.stardivision.impress', + smf: 'application/vnd.stardivision.math', + sdw: 'application/vnd.stardivision.writer', + sgl: 'application/vnd.stardivision.writer-global', + sm: 'application/vnd.stepmania.stepchart', + sit: 'application/x-stuffit', + sitx: 'application/x-stuffitx', + sdkm: 'application/vnd.solent.sdkm+xml', + xo: 'application/vnd.olpc-sugar', + au: 'audio/basic', + wqd: 'application/vnd.wqd', + sis: 'application/vnd.symbian.install', + smi: 'application/smil+xml', + xsm: 'application/vnd.syncml+xml', + bdm: 'application/vnd.syncml.dm+wbxml', + xdm: 'application/vnd.syncml.dm+xml', + sv4cpio: 'application/x-sv4cpio', + sv4crc: 'application/x-sv4crc', + sbml: 'application/sbml+xml', + tsv: 'text/tab-separated-values', + tiff: 'image/tiff', + tao: 'application/vnd.tao.intent-module-archive', + tar: 'application/x-tar', + tcl: 'application/x-tcl', + tex: 'application/x-tex', + tfm: 'application/x-tex-tfm', + tei: 'application/tei+xml', + txt: 'text/plain', + dxp: 'application/vnd.spotfire.dxp', + sfs: 'application/vnd.spotfire.sfs', + tsd: 'application/timestamped-data', + tpt: 'application/vnd.trid.tpt', + mxs: 'application/vnd.triscape.mxs', + t: 'text/troff', + tra: 'application/vnd.trueapp', + ttf: 'application/x-font-ttf', + ttl: 'text/turtle', + umj: 'application/vnd.umajin', + uoml: 'application/vnd.uoml+xml', + unityweb: 'application/vnd.unity', + ufd: 'application/vnd.ufdl', + uri: 'text/uri-list', + utz: 'application/vnd.uiq.theme', + ustar: 'application/x-ustar', + uu: 'text/x-uuencode', + vcs: 'text/x-vcalendar', + vcf: 'text/x-vcard', + vcd: 'application/x-cdlink', + vsf: 'application/vnd.vsf', + wrl: 'model/vrml', + vcx: 'application/vnd.vcx', + mts: 'model/vnd.mts', + vtu: 'model/vnd.vtu', + vis: 'application/vnd.visionary', + viv: 'video/vnd.vivo', + ccxml: 'application/ccxml+xml,', + vxml: 'application/voicexml+xml', + src: 'application/x-wais-source', + wbxml: 'application/vnd.wap.wbxml', + wbmp: 'image/vnd.wap.wbmp', + wav: 'audio/x-wav', + davmount: 'application/davmount+xml', + woff: 'application/x-font-woff', + wspolicy: 'application/wspolicy+xml', + webp: 'image/webp', + wtb: 'application/vnd.webturbo', + wgt: 'application/widget', + hlp: 'application/winhlp', + wml: 'text/vnd.wap.wml', + wmls: 'text/vnd.wap.wmlscript', + wmlsc: 'application/vnd.wap.wmlscriptc', + wpd: 'application/vnd.wordperfect', + stf: 'application/vnd.wt.stf', + wsdl: 'application/wsdl+xml', + xbm: 'image/x-xbitmap', + xpm: 'image/x-xpixmap', + xwd: 'image/x-xwindowdump', + der: 'application/x-x509-ca-cert', + fig: 'application/x-xfig', + xhtml: 'application/xhtml+xml', + xml: 'application/xml', + xdf: 'application/xcap-diff+xml', + xenc: 'application/xenc+xml', + xer: 'application/patch-ops-error+xml', + rl: 'application/resource-lists+xml', + rs: 'application/rls-services+xml', + rld: 'application/resource-lists-diff+xml', + xslt: 'application/xslt+xml', + xop: 'application/xop+xml', + xpi: 'application/x-xpinstall', + xspf: 'application/xspf+xml', + xul: 'application/vnd.mozilla.xul+xml', + xyz: 'chemical/x-xyz', + yaml: 'text/yaml', + yang: 'application/yang', + yin: 'application/yin+xml', + zir: 'application/vnd.zul', + zip: 'application/zip', + zmm: 'application/vnd.handheld-entertainment+xml', + zaz: 'application/vnd.zzazz.deck+xml' +}; const mimeIcons = { - 'pdf': 'mdi-pdf-box', + pdf: 'mdi-pdf-box', - 'docx': 'mdi-file-word-outline', - 'dotx': 'mdi-file-word-outline', - "odt": "mdi-file-word-outline", - "odm": "mdi-file-word-outline", - "ott": "mdi-file-word-outline", + docx: 'mdi-file-word-outline', + dotx: 'mdi-file-word-outline', + odt: 'mdi-file-word-outline', + odm: 'mdi-file-word-outline', + ott: 'mdi-file-word-outline', - 'ppt': 'mdi-file-powerpoint-box', - "pptx": "mdi-file-powerpoint-box", - "sldx": "mdi-file-powerpoint-box", - "ppsx": "mdi-file-powerpoint-box", - "potx": "mdi-file-powerpoint-box", + ppt: 'mdi-file-powerpoint-box', + pptx: 'mdi-file-powerpoint-box', + sldx: 'mdi-file-powerpoint-box', + ppsx: 'mdi-file-powerpoint-box', + potx: 'mdi-file-powerpoint-box', - "xls": "mdi-file-excel-outline", - "xlam": "mdi-file-excel-outline", - "xlsb": "mdi-file-excel-outline", - "xltm": "mdi-file-excel-outline", - "xlsm": 'mdi-file-excel-outline' + xls: 'mdi-file-excel-outline', + xlam: 'mdi-file-excel-outline', + xlsb: 'mdi-file-excel-outline', + xltm: 'mdi-file-excel-outline', + xlsm: 'mdi-file-excel-outline' +}; -} - - -export { - mimeIcons -}/** +export { mimeIcons }; +/** * @copyright Copyright (c) 2021, Xgene Cloud Ltd * * @author Naveen MR diff --git a/packages/nocodb/src/lib/utils/projectAcl.ts b/packages/nocodb/src/lib/utils/projectAcl.ts index 62e9a4d01a..e1498cd33c 100644 --- a/packages/nocodb/src/lib/utils/projectAcl.ts +++ b/packages/nocodb/src/lib/utils/projectAcl.ts @@ -74,14 +74,12 @@ export default { functionMetaDelete: true, functionMetaRecreate: true, - tableCreateStatement: true, tableInsertStatement: true, tableUpdateStatement: true, tableSelectStatement: true, tableDeleteStatement: true, - tableList: true, viewList: true, functionList: true, @@ -161,7 +159,6 @@ export default { grpcProtoDownloadZip: true, projectUpdateByWeb: false, - xcModelRowAuditAndCommentList: true, xcAuditCommentInsert: true, xcAuditModelCommentsCount: true @@ -171,46 +168,45 @@ export default { projectList: true, PROJECT_READ_BY_WEB: true, - - 'tableXcModelGet': true, - 'xcRelationList': true, - 'tableList': true, - 'viewList': true, - 'functionList': true, - 'sequenceList': true, - 'procedureList': true, - 'columnList': true, - 'triggerList': true, - 'relationList': true, - 'relationListAll': true, - 'indexList': true, - 'list': true, + tableXcModelGet: true, + xcRelationList: true, + tableList: true, + viewList: true, + functionList: true, + sequenceList: true, + procedureList: true, + columnList: true, + triggerList: true, + relationList: true, + relationListAll: true, + indexList: true, + list: true, xcModelRowAuditAndCommentList: true, xcAuditCommentInsert: true, xcAuditModelCommentsCount: true - }, viewer: { + }, + viewer: { xcVirtualTableList: true, projectList: true, PROJECT_READ_BY_WEB: true, - - 'tableXcModelGet': true, - 'xcRelationList': true, - 'tableList': true, - 'viewList': true, - 'functionList': true, - 'sequenceList': true, - 'procedureList': true, - 'columnList': true, - 'triggerList': true, - 'relationList': true, - 'relationListAll': true, - 'indexList': true, - 'list': true, + tableXcModelGet: true, + xcRelationList: true, + tableList: true, + viewList: true, + functionList: true, + sequenceList: true, + procedureList: true, + columnList: true, + triggerList: true, + relationList: true, + relationListAll: true, + indexList: true, + list: true }, user_new: { - projectList: true, + projectList: true }, user: { projectList: true, @@ -225,8 +221,7 @@ export default { xcMetaTablesImportZipToLocalFsAndDb: true, xcMetaTablesExportDbToZip: true } -} - +}; const readOperations = [ 'testConnection', @@ -279,9 +274,8 @@ const readOperations = [ 'testConnection', 'projectChangeEnv', 'xcRoutesPolicyAllGet', - 'grpcProtoDownloadZip', - -] + 'grpcProtoDownloadZip' +]; const writeOperations = [ 'tableMetaCreate', @@ -385,12 +379,10 @@ const writeOperations = [ 'tableRename', 'projectUpdateByWeb', 'createSharedViewLink' -] +]; -export { - readOperations, - writeOperations -}/** +export { readOperations, writeOperations }; +/** * @copyright Copyright (c) 2021, Xgene Cloud Ltd * * @author Naveen MR diff --git a/packages/nocodb/src/plugins/backblaze/Backblaze.ts b/packages/nocodb/src/plugins/backblaze/Backblaze.ts index aa8c68e647..1b86a1dee0 100644 --- a/packages/nocodb/src/plugins/backblaze/Backblaze.ts +++ b/packages/nocodb/src/plugins/backblaze/Backblaze.ts @@ -1,11 +1,10 @@ -import fs from "fs"; -import path from "path"; +import fs from 'fs'; +import path from 'path'; -import AWS from "aws-sdk"; -import {IStorageAdapter, XcFile} from "nc-plugin"; +import AWS from 'aws-sdk'; +import { IStorageAdapter, XcFile } from 'nc-plugin'; export default class Backblaze implements IStorageAdapter { - private s3Client: AWS.S3; private input: any; @@ -13,16 +12,14 @@ export default class Backblaze implements IStorageAdapter { this.input = input; } - async fileCreate(key: string, file: XcFile): Promise { - const uploadParams: any = { ACL: 'public-read' }; return new Promise((resolve, reject) => { // Configure the file stream and obtain the upload parameters const fileStream = fs.createReadStream(file.path); - fileStream.on('error', (err) => { + fileStream.on('error', err => { console.log('File Error', err); reject(err); }); @@ -33,7 +30,7 @@ export default class Backblaze implements IStorageAdapter { // call S3 to retrieve upload file to specified bucket this.s3Client.upload(uploadParams, (err, data) => { if (err) { - console.log("Error", err); + console.log('Error', err); reject(err); } if (data) { @@ -49,7 +46,7 @@ export default class Backblaze implements IStorageAdapter { public async fileRead(key: string): Promise { return new Promise((resolve, reject) => { - this.s3Client.getObject({Key: key} as any, (err, data) => { + this.s3Client.getObject({ Key: key } as any, (err, data) => { if (err) { return reject(err); } @@ -62,19 +59,19 @@ export default class Backblaze implements IStorageAdapter { } public async init(): Promise { - const s3Options: any = { - params: {Bucket: this.input.bucket}, + params: { Bucket: this.input.bucket }, region: this.input.region }; - s3Options.accessKeyId = this.input.access_key + s3Options.accessKeyId = this.input.access_key; s3Options.secretAccessKey = this.input.access_secret; - s3Options.endpoint = new AWS.Endpoint(`${this.input.region}.backblazeb2.com`); + s3Options.endpoint = new AWS.Endpoint( + `${this.input.region}.backblazeb2.com` + ); this.s3Client = new AWS.S3(s3Options); - } public async test(): Promise { @@ -94,10 +91,8 @@ export default class Backblaze implements IStorageAdapter { throw e; } } - } - /** * @copyright Copyright (c) 2021, Xgene Cloud Ltd * diff --git a/packages/nocodb/src/plugins/backblaze/BackblazePlugin.ts b/packages/nocodb/src/plugins/backblaze/BackblazePlugin.ts index 7ace376f6d..57efc27b3f 100644 --- a/packages/nocodb/src/plugins/backblaze/BackblazePlugin.ts +++ b/packages/nocodb/src/plugins/backblaze/BackblazePlugin.ts @@ -1,13 +1,11 @@ -import {IStorageAdapter, XcStoragePlugin} from "nc-plugin"; - -import Backblaze from "./Backblaze"; +import { IStorageAdapter, XcStoragePlugin } from 'nc-plugin'; +import Backblaze from './Backblaze'; class BackblazePlugin extends XcStoragePlugin { - private static storageAdapter: Backblaze; - public getAdapter(): IStorageAdapter { + public getAdapter(): IStorageAdapter { return BackblazePlugin.storageAdapter; } @@ -15,7 +13,6 @@ class BackblazePlugin extends XcStoragePlugin { BackblazePlugin.storageAdapter = new Backblaze(config); await BackblazePlugin.storageAdapter.init(); } - } export default BackblazePlugin; diff --git a/packages/nocodb/src/plugins/backblaze/index.ts b/packages/nocodb/src/plugins/backblaze/index.ts index 49a0127bbd..453cb41bfe 100644 --- a/packages/nocodb/src/plugins/backblaze/index.ts +++ b/packages/nocodb/src/plugins/backblaze/index.ts @@ -1,59 +1,69 @@ -import {XcActionType, XcType} from "nc-common"; -import {XcPluginConfig} from "nc-plugin"; +import { XcActionType, XcType } from 'nc-common'; +import { XcPluginConfig } from 'nc-plugin'; -import BackblazePlugin from "./BackblazePlugin"; +import BackblazePlugin from './BackblazePlugin'; -const config: XcPluginConfig ={ +const config: XcPluginConfig = { builder: BackblazePlugin, title: 'Backblaze B2', version: '0.0.1', logo: 'plugins/backblaze.jpeg', tags: 'Storage', - description:'Backblaze B2 is enterprise-grade, S3 compatible storage that companies around the world use to store and serve data while improving their cloud OpEx vs. Amazon S3 and others.', - inputs:{ + description: + 'Backblaze B2 is enterprise-grade, S3 compatible storage that companies around the world use to store and serve data while improving their cloud OpEx vs. Amazon S3 and others.', + inputs: { title: 'Configure Backblaze B2', - items: [{ - key: 'bucket', - label: 'Bucket Name', - placeholder: 'Bucket Name', - type: XcType.SingleLineText, - required: true - }, { - key: 'region', - label: 'Region', - placeholder: 'Region', - type: XcType.SingleLineText, - required: true - }, { - key: 'access_key', - label: 'Access Key', - placeholder: 'Access Key', - type: XcType.SingleLineText, - required: true - }, { - key: 'access_secret', - label: 'Access Secret', - placeholder: 'Access Secret', - type: XcType.Password, - required: true - },], - actions: [{ - label: 'Test', - placeholder: 'Test', - key: 'test', - actionType: XcActionType.TEST, - type: XcType.Button - }, { - label: 'Save', - placeholder: 'Save', - key: 'save', - actionType: XcActionType.SUBMIT, - type: XcType.Button - },], - msgOnInstall:'Successfully installed and attachment will be stored in Backblaze B2', - msgOnUninstall:'', + items: [ + { + key: 'bucket', + label: 'Bucket Name', + placeholder: 'Bucket Name', + type: XcType.SingleLineText, + required: true + }, + { + key: 'region', + label: 'Region', + placeholder: 'Region', + type: XcType.SingleLineText, + required: true + }, + { + key: 'access_key', + label: 'Access Key', + placeholder: 'Access Key', + type: XcType.SingleLineText, + required: true + }, + { + key: 'access_secret', + label: 'Access Secret', + placeholder: 'Access Secret', + type: XcType.Password, + required: true + } + ], + actions: [ + { + label: 'Test', + placeholder: 'Test', + key: 'test', + actionType: XcActionType.TEST, + type: XcType.Button + }, + { + label: 'Save', + placeholder: 'Save', + key: 'save', + actionType: XcActionType.SUBMIT, + type: XcType.Button + } + ], + msgOnInstall: + 'Successfully installed and attachment will be stored in Backblaze B2', + msgOnUninstall: '' }, - category:'Storage', -} + category: 'Storage' +}; -export default config; \ No newline at end of file +export default config; diff --git a/packages/nocodb/src/plugins/discord/Discord.ts b/packages/nocodb/src/plugins/discord/Discord.ts index e1012a0b37..93f88042e7 100644 --- a/packages/nocodb/src/plugins/discord/Discord.ts +++ b/packages/nocodb/src/plugins/discord/Discord.ts @@ -1,29 +1,24 @@ -import axios from "axios"; -import {IWebhookNotificationAdapter} from "nc-plugin"; - +import axios from 'axios'; +import { IWebhookNotificationAdapter } from 'nc-plugin'; export default class Discord implements IWebhookNotificationAdapter { public init(): Promise { return Promise.resolve(undefined); } - public async sendMessage(content: string, payload:any): Promise { - for (const {webhook_url} of payload ?.channels) { + public async sendMessage(content: string, payload: any): Promise { + for (const { webhook_url } of payload?.channels) { try { await axios.post(webhook_url, { content - }) + }); } catch (e) { - console.log(e) + console.log(e); } } } - - - } - /** * @copyright Copyright (c) 2021, Xgene Cloud Ltd * diff --git a/packages/nocodb/src/plugins/discord/DiscordPlugin.ts b/packages/nocodb/src/plugins/discord/DiscordPlugin.ts index 8965aeb83f..73d3dcd5e5 100644 --- a/packages/nocodb/src/plugins/discord/DiscordPlugin.ts +++ b/packages/nocodb/src/plugins/discord/DiscordPlugin.ts @@ -1,13 +1,14 @@ -import {IWebhookNotificationAdapter, XcWebhookNotificationPlugin} from "nc-plugin"; - -import Discord from "./Discord"; +import { + IWebhookNotificationAdapter, + XcWebhookNotificationPlugin +} from 'nc-plugin'; +import Discord from './Discord'; class DiscordPlugin extends XcWebhookNotificationPlugin { - private static notificationAdapter: Discord; - public getAdapter(): IWebhookNotificationAdapter { + public getAdapter(): IWebhookNotificationAdapter { return DiscordPlugin.notificationAdapter; } @@ -15,7 +16,6 @@ class DiscordPlugin extends XcWebhookNotificationPlugin { DiscordPlugin.notificationAdapter = new Discord(); await DiscordPlugin.notificationAdapter.init(); } - } export default DiscordPlugin; diff --git a/packages/nocodb/src/plugins/discord/index.ts b/packages/nocodb/src/plugins/discord/index.ts index bbe38209c2..11aa1f6bfb 100644 --- a/packages/nocodb/src/plugins/discord/index.ts +++ b/packages/nocodb/src/plugins/discord/index.ts @@ -1,49 +1,57 @@ -import {XcActionType, XcType} from "nc-common"; -import {XcPluginConfig} from "nc-plugin"; +import { XcActionType, XcType } from 'nc-common'; +import { XcPluginConfig } from 'nc-plugin'; -import DiscordPlugin from "./DiscordPlugin"; +import DiscordPlugin from './DiscordPlugin'; -const config: XcPluginConfig ={ +const config: XcPluginConfig = { builder: DiscordPlugin, title: 'Discord', version: '0.0.1', logo: 'plugins/discord.png', - description: 'Discord is the easiest way to talk over voice, video, and text. Talk, chat, hang out, and stay close with your friends and communities.', + description: + 'Discord is the easiest way to talk over voice, video, and text. Talk, chat, hang out, and stay close with your friends and communities.', price: 'Free', tags: 'Chat', category: 'Chat', - inputs:{ + inputs: { title: 'Configure Discord', array: true, - items: [{ - key: 'channel', - label: 'Channel Name', - placeholder: 'Channel Name', - type: XcType.SingleLineText, - required: true - }, { - key: 'webhook_url', - label: 'Webhook URL', - type: XcType.Password, - placeholder: 'Webhook URL', - required: true - }], - actions: [{ - label: 'Test', - placeholder: 'Test', - key: 'test', - actionType: XcActionType.TEST, - type: XcType.Button - }, { - label: 'Save', - placeholder: 'Save', - key: 'save', - actionType: XcActionType.SUBMIT, - type: XcType.Button - },], - msgOnInstall:'Successfully installed and Discord is enabled for notification.', - msgOnUninstall:'', + items: [ + { + key: 'channel', + label: 'Channel Name', + placeholder: 'Channel Name', + type: XcType.SingleLineText, + required: true + }, + { + key: 'webhook_url', + label: 'Webhook URL', + type: XcType.Password, + placeholder: 'Webhook URL', + required: true + } + ], + actions: [ + { + label: 'Test', + placeholder: 'Test', + key: 'test', + actionType: XcActionType.TEST, + type: XcType.Button + }, + { + label: 'Save', + placeholder: 'Save', + key: 'save', + actionType: XcActionType.SUBMIT, + type: XcType.Button + } + ], + msgOnInstall: + 'Successfully installed and Discord is enabled for notification.', + msgOnUninstall: '' } -} +}; -export default config; \ No newline at end of file +export default config; diff --git a/packages/nocodb/src/plugins/gcs/Gcs.ts b/packages/nocodb/src/plugins/gcs/Gcs.ts index cb702fd4df..f83ec6c91f 100644 --- a/packages/nocodb/src/plugins/gcs/Gcs.ts +++ b/packages/nocodb/src/plugins/gcs/Gcs.ts @@ -1,11 +1,10 @@ -import fs from "fs"; -import path from "path"; +import fs from 'fs'; +import path from 'path'; -import {Storage, StorageOptions} from '@google-cloud/storage'; -import {IStorageAdapter, XcFile} from "nc-plugin"; +import { Storage, StorageOptions } from '@google-cloud/storage'; +import { IStorageAdapter, XcFile } from 'nc-plugin'; export default class Gcs implements IStorageAdapter { - private storageClient: Storage; private bucketName: string; private input: any; @@ -15,20 +14,21 @@ export default class Gcs implements IStorageAdapter { } async fileCreate(key: string, file: XcFile): Promise { - - const uploadResponse = await this.storageClient.bucket(this.bucketName).upload(file.path, { - destination: key, - // Support for HTTP requests made with `Accept-Encoding: gzip` - gzip: true, - // By setting the option `destination`, you can change the name of the - // object you are uploading to a bucket. - metadata: { - // Enable long-lived HTTP caching headers - // Use only if the contents of the file will never change - // (If the contents will change, use cacheControl: 'no-cache') - cacheControl: 'public, max-age=31536000' - }, - }); + const uploadResponse = await this.storageClient + .bucket(this.bucketName) + .upload(file.path, { + destination: key, + // Support for HTTP requests made with `Accept-Encoding: gzip` + gzip: true, + // By setting the option `destination`, you can change the name of the + // object you are uploading to a bucket. + metadata: { + // Enable long-lived HTTP caching headers + // Use only if the contents of the file will never change + // (If the contents will change, use cacheControl: 'no-cache') + cacheControl: 'public, max-age=31536000' + } + }); return uploadResponse[0].publicUrl(); } @@ -38,7 +38,6 @@ export default class Gcs implements IStorageAdapter { } public fileRead(key: string): Promise { - return new Promise((resolve, reject) => { const file = this.storageClient.bucket(this.bucketName).file(key); // Check for existence, since gcloud-node seemed to be caching the result @@ -58,7 +57,6 @@ export default class Gcs implements IStorageAdapter { } public async init(): Promise { - const options: StorageOptions = {}; // options.credentials = { @@ -70,14 +68,13 @@ export default class Gcs implements IStorageAdapter { options.credentials = { client_email: this.input.client_email, private_key: this.input.private_key - } + }; this.bucketName = this.input.bucket; this.storageClient = new Storage(options); } - public async test(): Promise { try { const tempFile = path.join(process.cwd(), 'temp.txt'); diff --git a/packages/nocodb/src/plugins/gcs/GcsPlugin.ts b/packages/nocodb/src/plugins/gcs/GcsPlugin.ts index 3b77d0d6ef..64b456312c 100644 --- a/packages/nocodb/src/plugins/gcs/GcsPlugin.ts +++ b/packages/nocodb/src/plugins/gcs/GcsPlugin.ts @@ -1,13 +1,11 @@ -import {IStorageAdapter, XcStoragePlugin} from "nc-plugin"; - -import Gcs from "./Gcs"; +import { IStorageAdapter, XcStoragePlugin } from 'nc-plugin'; +import Gcs from './Gcs'; class GcsPlugin extends XcStoragePlugin { - private static storageAdapter: Gcs; - public getAdapter(): IStorageAdapter { + public getAdapter(): IStorageAdapter { return GcsPlugin.storageAdapter; } @@ -15,7 +13,6 @@ class GcsPlugin extends XcStoragePlugin { GcsPlugin.storageAdapter = new Gcs(config); await GcsPlugin.storageAdapter.init(); } - } export default GcsPlugin; diff --git a/packages/nocodb/src/plugins/gcs/index.ts b/packages/nocodb/src/plugins/gcs/index.ts index 73bbfb3266..be5ca4a266 100644 --- a/packages/nocodb/src/plugins/gcs/index.ts +++ b/packages/nocodb/src/plugins/gcs/index.ts @@ -1,54 +1,63 @@ -import {XcActionType, XcType} from "nc-common"; -import {XcPluginConfig} from "nc-plugin"; +import { XcActionType, XcType } from 'nc-common'; +import { XcPluginConfig } from 'nc-plugin'; -import GcsPlugin from "./GcsPlugin"; +import GcsPlugin from './GcsPlugin'; -const config: XcPluginConfig ={ +const config: XcPluginConfig = { builder: GcsPlugin, title: 'GCS', version: '0.0.1', logo: 'plugins/gcs.png', - description: 'Google Cloud Storage is a RESTful online file storage web service for storing and accessing data on Google Cloud Platform infrastructure.', + description: + 'Google Cloud Storage is a RESTful online file storage web service for storing and accessing data on Google Cloud Platform infrastructure.', price: 'Free', tags: 'Storage', category: 'Storage', - inputs:{ + inputs: { title: 'Configure Google Cloud Storage', - items: [{ - key: 'bucket', - label: 'Bucket Name', - placeholder: 'Bucket Name', - type: XcType.SingleLineText, - required: true - },{ - key: 'client_email', - label: 'Client Email', - placeholder: 'Client Email', - type: XcType.SingleLineText, - required: true - }, { - key: 'private_key', - label: 'Private Key', - placeholder: 'Private Key', - type: XcType.Password, - required: true - }, ], - actions: [{ - label: 'Test', - placeholder: 'Test', - key: 'test', - actionType: XcActionType.TEST, - type: XcType.Button - }, { - label: 'Save', - placeholder: 'Save', - key: 'save', - actionType: XcActionType.SUBMIT, - type: XcType.Button - },], - msgOnInstall:'Successfully installed and attachment will be stored in Google Cloud Storage', - msgOnUninstall:'', - }, -} + items: [ + { + key: 'bucket', + label: 'Bucket Name', + placeholder: 'Bucket Name', + type: XcType.SingleLineText, + required: true + }, + { + key: 'client_email', + label: 'Client Email', + placeholder: 'Client Email', + type: XcType.SingleLineText, + required: true + }, + { + key: 'private_key', + label: 'Private Key', + placeholder: 'Private Key', + type: XcType.Password, + required: true + } + ], + actions: [ + { + label: 'Test', + placeholder: 'Test', + key: 'test', + actionType: XcActionType.TEST, + type: XcType.Button + }, + { + label: 'Save', + placeholder: 'Save', + key: 'save', + actionType: XcActionType.SUBMIT, + type: XcType.Button + } + ], + msgOnInstall: + 'Successfully installed and attachment will be stored in Google Cloud Storage', + msgOnUninstall: '' + } +}; -export default config; \ No newline at end of file +export default config; diff --git a/packages/nocodb/src/plugins/linode/LinodeObjectStorage.ts b/packages/nocodb/src/plugins/linode/LinodeObjectStorage.ts index e7afeb9176..a235dbd11d 100644 --- a/packages/nocodb/src/plugins/linode/LinodeObjectStorage.ts +++ b/packages/nocodb/src/plugins/linode/LinodeObjectStorage.ts @@ -1,11 +1,10 @@ -import fs from "fs"; -import path from "path"; +import fs from 'fs'; +import path from 'path'; -import AWS from "aws-sdk"; -import {IStorageAdapter, XcFile} from "nc-plugin"; +import AWS from 'aws-sdk'; +import { IStorageAdapter, XcFile } from 'nc-plugin'; export default class LinodeObjectStorage implements IStorageAdapter { - private s3Client: AWS.S3; private input: any; @@ -13,16 +12,14 @@ export default class LinodeObjectStorage implements IStorageAdapter { this.input = input; } - async fileCreate(key: string, file: XcFile): Promise { - const uploadParams: any = { ACL: 'public-read' }; return new Promise((resolve, reject) => { // Configure the file stream and obtain the upload parameters const fileStream = fs.createReadStream(file.path); - fileStream.on('error', (err) => { + fileStream.on('error', err => { console.log('File Error', err); reject(err); }); @@ -33,7 +30,7 @@ export default class LinodeObjectStorage implements IStorageAdapter { // call S3 to retrieve upload file to specified bucket this.s3Client.upload(uploadParams, (err, data) => { if (err) { - console.log("Error", err); + console.log('Error', err); reject(err); } if (data) { @@ -49,7 +46,7 @@ export default class LinodeObjectStorage implements IStorageAdapter { public async fileRead(key: string): Promise { return new Promise((resolve, reject) => { - this.s3Client.getObject({Key: key} as any, (err, data) => { + this.s3Client.getObject({ Key: key } as any, (err, data) => { if (err) { return reject(err); } @@ -63,14 +60,16 @@ export default class LinodeObjectStorage implements IStorageAdapter { public async init(): Promise { const s3Options: any = { - params: {Bucket: this.input.bucket}, + params: { Bucket: this.input.bucket }, region: this.input.region }; - s3Options.accessKeyId = this.input.access_key + s3Options.accessKeyId = this.input.access_key; s3Options.secretAccessKey = this.input.access_secret; - s3Options.endpoint = new AWS.Endpoint(`${this.input.region}.linodeobjects.com`); + s3Options.endpoint = new AWS.Endpoint( + `${this.input.region}.linodeobjects.com` + ); this.s3Client = new AWS.S3(s3Options); } @@ -92,10 +91,8 @@ export default class LinodeObjectStorage implements IStorageAdapter { throw e; } } - } - /** * @copyright Copyright (c) 2021, Xgene Cloud Ltd * diff --git a/packages/nocodb/src/plugins/linode/LinodeObjectStoragePlugin.ts b/packages/nocodb/src/plugins/linode/LinodeObjectStoragePlugin.ts index 52ef3387fc..c1462a22e8 100644 --- a/packages/nocodb/src/plugins/linode/LinodeObjectStoragePlugin.ts +++ b/packages/nocodb/src/plugins/linode/LinodeObjectStoragePlugin.ts @@ -1,13 +1,11 @@ -import {IStorageAdapter, XcStoragePlugin} from "nc-plugin"; - -import LinodeObjectStorage from "./LinodeObjectStorage"; +import { IStorageAdapter, XcStoragePlugin } from 'nc-plugin'; +import LinodeObjectStorage from './LinodeObjectStorage'; class LinodeObjectStoragePlugin extends XcStoragePlugin { - private static storageAdapter: LinodeObjectStorage; - public getAdapter(): IStorageAdapter { + public getAdapter(): IStorageAdapter { return LinodeObjectStoragePlugin.storageAdapter; } @@ -15,7 +13,6 @@ class LinodeObjectStoragePlugin extends XcStoragePlugin { LinodeObjectStoragePlugin.storageAdapter = new LinodeObjectStorage(config); await LinodeObjectStoragePlugin.storageAdapter.init(); } - } export default LinodeObjectStoragePlugin; diff --git a/packages/nocodb/src/plugins/linode/index.ts b/packages/nocodb/src/plugins/linode/index.ts index ed78da1911..95630278b0 100644 --- a/packages/nocodb/src/plugins/linode/index.ts +++ b/packages/nocodb/src/plugins/linode/index.ts @@ -1,7 +1,7 @@ -import {XcActionType, XcType} from "nc-common"; -import {XcPluginConfig} from "nc-plugin"; +import { XcActionType, XcType } from 'nc-common'; +import { XcPluginConfig } from 'nc-plugin'; -import LinodeObjectStoragePlugin from "./LinodeObjectStoragePlugin"; +import LinodeObjectStoragePlugin from './LinodeObjectStoragePlugin'; const config: XcPluginConfig = { builder: LinodeObjectStoragePlugin, @@ -9,51 +9,61 @@ const config: XcPluginConfig = { version: '0.0.1', logo: 'plugins/linode.svg', tags: 'Storage', - description: 'S3-compatible Linode Object Storage makes it easy and more affordable to manage unstructured data such as content assets, as well as sophisticated and data-intensive storage challenges around artificial intelligence and machine learning.', + description: + 'S3-compatible Linode Object Storage makes it easy and more affordable to manage unstructured data such as content assets, as well as sophisticated and data-intensive storage challenges around artificial intelligence and machine learning.', inputs: { title: 'Configure Linode Object Storage', - items: [{ - key: 'bucket', - label: 'Bucket Name', - placeholder: 'Bucket Name', - type: XcType.SingleLineText, - required: true - }, { - key: 'region', - label: 'Region', - placeholder: 'Region', - type: XcType.SingleLineText, - required: true - }, { - key: 'access_key', - label: 'Access Key', - placeholder: 'Access Key', - type: XcType.SingleLineText, - required: true - }, { - key: 'access_secret', - label: 'Access Secret', - placeholder: 'Access Secret', - type: XcType.Password, - required: true - },], - actions: [{ - label: 'Test', - placeholder: 'Test', - key: 'test', - actionType: XcActionType.TEST, - type: XcType.Button - }, { - label: 'Save', - placeholder: 'Save', - key: 'save', - actionType: XcActionType.SUBMIT, - type: XcType.Button - },], - msgOnInstall: 'Successfully installed and attachment will be stored in Linode Object Storage', - msgOnUninstall: '', + items: [ + { + key: 'bucket', + label: 'Bucket Name', + placeholder: 'Bucket Name', + type: XcType.SingleLineText, + required: true + }, + { + key: 'region', + label: 'Region', + placeholder: 'Region', + type: XcType.SingleLineText, + required: true + }, + { + key: 'access_key', + label: 'Access Key', + placeholder: 'Access Key', + type: XcType.SingleLineText, + required: true + }, + { + key: 'access_secret', + label: 'Access Secret', + placeholder: 'Access Secret', + type: XcType.Password, + required: true + } + ], + actions: [ + { + label: 'Test', + placeholder: 'Test', + key: 'test', + actionType: XcActionType.TEST, + type: XcType.Button + }, + { + label: 'Save', + placeholder: 'Save', + key: 'save', + actionType: XcActionType.SUBMIT, + type: XcType.Button + } + ], + msgOnInstall: + 'Successfully installed and attachment will be stored in Linode Object Storage', + msgOnUninstall: '' }, - category: 'Storage', -} + category: 'Storage' +}; -export default config; \ No newline at end of file +export default config; diff --git a/packages/nocodb/src/plugins/mailerSend/MailerSend.ts b/packages/nocodb/src/plugins/mailerSend/MailerSend.ts index 600fdbe1cb..a0911fd2f9 100644 --- a/packages/nocodb/src/plugins/mailerSend/MailerSend.ts +++ b/packages/nocodb/src/plugins/mailerSend/MailerSend.ts @@ -1,11 +1,9 @@ -import {IEmailAdapter} from "nc-plugin"; -import MailerSend, {EmailParams, Recipient} from "mailersend"; +import { IEmailAdapter } from 'nc-plugin'; +import MailerSend, { EmailParams, Recipient } from 'mailersend'; - -import {XcEmail} from "../../interface/IEmailAdapter"; +import { XcEmail } from '../../interface/IEmailAdapter'; export default class Mailer implements IEmailAdapter { - private mailersend: MailerSend; private input: any; @@ -15,15 +13,12 @@ export default class Mailer implements IEmailAdapter { public async init(): Promise { this.mailersend = new MailerSend({ - api_key: this.input?.api_key, - }) + api_key: this.input?.api_key + }); } public async mailSend(mail: XcEmail): Promise { - - const recipients = [ - new Recipient(mail.to) - ]; + const recipients = [new Recipient(mail.to)]; const emailParams = new EmailParams() .setFrom(this.input.from) @@ -33,9 +28,9 @@ export default class Mailer implements IEmailAdapter { .setHtml(mail.html) .setText(mail.text); - const res = await this.mailersend.send(emailParams) + const res = await this.mailersend.send(emailParams); if (res.status === 401) { - throw new Error(res.status) + throw new Error(res.status); } } @@ -43,18 +38,16 @@ export default class Mailer implements IEmailAdapter { try { await this.mailSend({ to: email, - subject: "Test email", + subject: 'Test email', html: 'Test email' - } as any) + } as any); return true; } catch (e) { throw e; } } - } - /** * @copyright Copyright (c) 2021, Xgene Cloud Ltd * diff --git a/packages/nocodb/src/plugins/mailerSend/MailerSendPlugin.ts b/packages/nocodb/src/plugins/mailerSend/MailerSendPlugin.ts index b59c23341f..d7888d9a25 100644 --- a/packages/nocodb/src/plugins/mailerSend/MailerSendPlugin.ts +++ b/packages/nocodb/src/plugins/mailerSend/MailerSendPlugin.ts @@ -1,13 +1,11 @@ -import {IEmailAdapter, XcEmailPlugin} from "nc-plugin"; - -import MailerSend from "./MailerSend"; +import { IEmailAdapter, XcEmailPlugin } from 'nc-plugin'; +import MailerSend from './MailerSend'; class MailerSendPlugin extends XcEmailPlugin { - private static storageAdapter: MailerSend; - public getAdapter(): IEmailAdapter { + public getAdapter(): IEmailAdapter { return MailerSendPlugin.storageAdapter; } @@ -15,7 +13,6 @@ class MailerSendPlugin extends XcEmailPlugin { MailerSendPlugin.storageAdapter = new MailerSend(config); await MailerSendPlugin.storageAdapter.init(); } - } export default MailerSendPlugin; diff --git a/packages/nocodb/src/plugins/mailerSend/index.ts b/packages/nocodb/src/plugins/mailerSend/index.ts index 3ad7abfc70..24db8bb911 100644 --- a/packages/nocodb/src/plugins/mailerSend/index.ts +++ b/packages/nocodb/src/plugins/mailerSend/index.ts @@ -1,9 +1,9 @@ -import {XcActionType, XcType} from "nc-common"; -import {XcPluginConfig} from "nc-plugin"; +import { XcActionType, XcType } from 'nc-common'; +import { XcPluginConfig } from 'nc-plugin'; -import MailerSendPlugin from "./MailerSendPlugin"; +import MailerSendPlugin from './MailerSendPlugin'; -const config: XcPluginConfig ={ +const config: XcPluginConfig = { builder: MailerSendPlugin, title: 'MailerSend', version: '0.0.1', @@ -13,41 +13,49 @@ const config: XcPluginConfig ={ price: 'Free', tags: 'Email', category: 'Email', - inputs:{ + inputs: { title: 'Configure MailerSend', - items: [{ - key: 'api_key', - label: 'API KEy', - placeholder: 'eg: ***************', - type: XcType.Password, - required: true - },{ - key: 'from', - label: 'From', - placeholder: 'eg: admin@example.com', - type: XcType.SingleLineText, - required: true - }, { - key: 'from_name', - label: 'From Name', - placeholder: 'eg: Adam', - type: XcType.SingleLineText, - required: true - }, ], - actions: [{ - label: 'Test', - key: 'test', - actionType: XcActionType.TEST, - type: XcType.Button - }, { - label: 'Save', - key: 'save', - actionType: XcActionType.SUBMIT, - type: XcType.Button - },], - msgOnInstall: 'Successfully installed and email notification will use MailerSend configuration', - msgOnUninstall: '', + items: [ + { + key: 'api_key', + label: 'API KEy', + placeholder: 'eg: ***************', + type: XcType.Password, + required: true + }, + { + key: 'from', + label: 'From', + placeholder: 'eg: admin@example.com', + type: XcType.SingleLineText, + required: true + }, + { + key: 'from_name', + label: 'From Name', + placeholder: 'eg: Adam', + type: XcType.SingleLineText, + required: true + } + ], + actions: [ + { + label: 'Test', + key: 'test', + actionType: XcActionType.TEST, + type: XcType.Button + }, + { + label: 'Save', + key: 'save', + actionType: XcActionType.SUBMIT, + type: XcType.Button + } + ], + msgOnInstall: + 'Successfully installed and email notification will use MailerSend configuration', + msgOnUninstall: '' } -} +}; -export default config; \ No newline at end of file +export default config; diff --git a/packages/nocodb/src/plugins/mattermost/Mattermost.ts b/packages/nocodb/src/plugins/mattermost/Mattermost.ts index 57730eaefa..64ae1492fe 100644 --- a/packages/nocodb/src/plugins/mattermost/Mattermost.ts +++ b/packages/nocodb/src/plugins/mattermost/Mattermost.ts @@ -1,6 +1,5 @@ -import axios from "axios"; -import {IWebhookNotificationAdapter} from "nc-plugin"; - +import axios from 'axios'; +import { IWebhookNotificationAdapter } from 'nc-plugin'; export default class Mattermost implements IWebhookNotificationAdapter { public init(): Promise { @@ -8,21 +7,18 @@ export default class Mattermost implements IWebhookNotificationAdapter { } public async sendMessage(text: string, payload: any): Promise { - for (const {webhook_url} of payload?.channels) { + for (const { webhook_url } of payload?.channels) { try { await axios.post(webhook_url, { text - }) + }); } catch (e) { - console.log(e) + console.log(e); } } } - - } - /** * @copyright Copyright (c) 2021, Xgene Cloud Ltd * diff --git a/packages/nocodb/src/plugins/mattermost/MattermostPlugin.ts b/packages/nocodb/src/plugins/mattermost/MattermostPlugin.ts index e07125134a..726a2a0017 100644 --- a/packages/nocodb/src/plugins/mattermost/MattermostPlugin.ts +++ b/packages/nocodb/src/plugins/mattermost/MattermostPlugin.ts @@ -1,13 +1,14 @@ -import {IWebhookNotificationAdapter, XcWebhookNotificationPlugin} from "nc-plugin"; - -import Mattermost from "./Mattermost"; +import { + IWebhookNotificationAdapter, + XcWebhookNotificationPlugin +} from 'nc-plugin'; +import Mattermost from './Mattermost'; class MattermostPlugin extends XcWebhookNotificationPlugin { - private static notificationAdapter: Mattermost; - public getAdapter(): IWebhookNotificationAdapter { + public getAdapter(): IWebhookNotificationAdapter { return MattermostPlugin.notificationAdapter; } @@ -15,7 +16,6 @@ class MattermostPlugin extends XcWebhookNotificationPlugin { MattermostPlugin.notificationAdapter = new Mattermost(); await MattermostPlugin.notificationAdapter.init(); } - } export default MattermostPlugin; diff --git a/packages/nocodb/src/plugins/mattermost/index.ts b/packages/nocodb/src/plugins/mattermost/index.ts index 0af3da22ba..5272934a65 100644 --- a/packages/nocodb/src/plugins/mattermost/index.ts +++ b/packages/nocodb/src/plugins/mattermost/index.ts @@ -1,49 +1,57 @@ -import {XcActionType, XcType} from "nc-common"; -import {XcPluginConfig} from "nc-plugin"; +import { XcActionType, XcType } from 'nc-common'; +import { XcPluginConfig } from 'nc-plugin'; -import MattermostPlugin from "./MattermostPlugin"; +import MattermostPlugin from './MattermostPlugin'; -const config: XcPluginConfig ={ +const config: XcPluginConfig = { builder: MattermostPlugin, title: 'Mattermost', version: '0.0.1', logo: 'plugins/mattermost.png', - description: 'Mattermost brings all your team communication into one place, making it searchable and accessible anywhere.', + description: + 'Mattermost brings all your team communication into one place, making it searchable and accessible anywhere.', price: 'Free', tags: 'Chat', category: 'Chat', - inputs:{ + inputs: { title: 'Configure Mattermost', array: true, - items: [{ - key: 'channel', - label: 'Channel Name', - placeholder: 'Channel Name', - type: XcType.SingleLineText, - required: true - }, { - key: 'webhook_url', - label: 'Webhook URL', - placeholder: 'Webhook URL', - type: XcType.Password, - required: true - },], - actions: [{ - label: 'Test', - placeholder: 'Test', - key: 'test', - actionType: XcActionType.TEST, - type: XcType.Button - }, { - label: 'Save', - placeholder: 'Save', - key: 'save', - actionType: XcActionType.SUBMIT, - type: XcType.Button - },], - msgOnInstall: 'Successfully installed and Mattermost is enabled for notification.', - msgOnUninstall: '', + items: [ + { + key: 'channel', + label: 'Channel Name', + placeholder: 'Channel Name', + type: XcType.SingleLineText, + required: true + }, + { + key: 'webhook_url', + label: 'Webhook URL', + placeholder: 'Webhook URL', + type: XcType.Password, + required: true + } + ], + actions: [ + { + label: 'Test', + placeholder: 'Test', + key: 'test', + actionType: XcActionType.TEST, + type: XcType.Button + }, + { + label: 'Save', + placeholder: 'Save', + key: 'save', + actionType: XcActionType.SUBMIT, + type: XcType.Button + } + ], + msgOnInstall: + 'Successfully installed and Mattermost is enabled for notification.', + msgOnUninstall: '' } -} +}; -export default config; \ No newline at end of file +export default config; diff --git a/packages/nocodb/src/plugins/mino/MinioPlugin.ts b/packages/nocodb/src/plugins/mino/MinioPlugin.ts index eef7db7156..ec47223239 100644 --- a/packages/nocodb/src/plugins/mino/MinioPlugin.ts +++ b/packages/nocodb/src/plugins/mino/MinioPlugin.ts @@ -1,13 +1,11 @@ -import {IStorageAdapter, XcStoragePlugin} from "nc-plugin"; - -import Minio from "./Minio"; +import { IStorageAdapter, XcStoragePlugin } from 'nc-plugin'; +import Minio from './Minio'; class MinioPlugin extends XcStoragePlugin { - private static storageAdapter: Minio; - public getAdapter(): IStorageAdapter { + public getAdapter(): IStorageAdapter { return MinioPlugin.storageAdapter; } @@ -15,7 +13,6 @@ class MinioPlugin extends XcStoragePlugin { MinioPlugin.storageAdapter = new Minio(config); await MinioPlugin.storageAdapter.init(); } - } export default MinioPlugin; diff --git a/packages/nocodb/src/plugins/mino/index.ts b/packages/nocodb/src/plugins/mino/index.ts index 23bb66bc35..8e7523600a 100644 --- a/packages/nocodb/src/plugins/mino/index.ts +++ b/packages/nocodb/src/plugins/mino/index.ts @@ -1,72 +1,84 @@ -import {XcActionType, XcType} from "nc-common"; -import {XcPluginConfig} from "nc-plugin"; +import { XcActionType, XcType } from 'nc-common'; +import { XcPluginConfig } from 'nc-plugin'; -import S3Plugin from "./MinioPlugin"; +import S3Plugin from './MinioPlugin'; -const config: XcPluginConfig ={ +const config: XcPluginConfig = { builder: S3Plugin, title: 'Minio', version: '0.0.1', logo: 'plugins/minio.png', - description: 'MinIO is a High Performance Object Storage released under Apache License v2.0. It is API compatible with Amazon S3 cloud storage service.', + description: + 'MinIO is a High Performance Object Storage released under Apache License v2.0. It is API compatible with Amazon S3 cloud storage service.', price: 'Free', tags: 'Storage', category: 'Storage', - inputs:{ + inputs: { title: 'Configure Minio', - items: [{ - key: 'endPoint', - label: 'Minio Endpoint', - placeholder: 'Minio Endpoint', - type: XcType.SingleLineText, - required: true - },{ - key: 'port', - label: 'Port', - placeholder: 'Port', - type: XcType.Number, - required: true - },{ - key: 'bucket', - label: 'Bucket Name', - placeholder: 'Bucket Name', - type: XcType.SingleLineText, - required: true - }, { - key: 'access_key', - label: 'Access Key', - placeholder: 'Access Key', - type: XcType.SingleLineText, - required: true - }, { - key: 'access_secret', - label: 'Access Secret', - placeholder: 'Access Secret', - type: XcType.Password, - required: true - }, { - key: 'useSSL', - label: 'Use SSL', - placeholder: 'Use SSL', - type: XcType.Checkbox, - required: true - },], - actions: [{ - label: 'Test', - placeholder: 'Test', - key: 'test', - actionType: XcActionType.TEST, - type: XcType.Button - }, { - label: 'Save', - placeholder: 'Save', - key: 'save', - actionType: XcActionType.SUBMIT, - type: XcType.Button - },], - msgOnInstall:'Successfully installed and attachment will be stored in Minio', - msgOnUninstall:'', + items: [ + { + key: 'endPoint', + label: 'Minio Endpoint', + placeholder: 'Minio Endpoint', + type: XcType.SingleLineText, + required: true + }, + { + key: 'port', + label: 'Port', + placeholder: 'Port', + type: XcType.Number, + required: true + }, + { + key: 'bucket', + label: 'Bucket Name', + placeholder: 'Bucket Name', + type: XcType.SingleLineText, + required: true + }, + { + key: 'access_key', + label: 'Access Key', + placeholder: 'Access Key', + type: XcType.SingleLineText, + required: true + }, + { + key: 'access_secret', + label: 'Access Secret', + placeholder: 'Access Secret', + type: XcType.Password, + required: true + }, + { + key: 'useSSL', + label: 'Use SSL', + placeholder: 'Use SSL', + type: XcType.Checkbox, + required: true + } + ], + actions: [ + { + label: 'Test', + placeholder: 'Test', + key: 'test', + actionType: XcActionType.TEST, + type: XcType.Button + }, + { + label: 'Save', + placeholder: 'Save', + key: 'save', + actionType: XcActionType.SUBMIT, + type: XcType.Button + } + ], + msgOnInstall: + 'Successfully installed and attachment will be stored in Minio', + msgOnUninstall: '' } -} +}; -export default config; \ No newline at end of file +export default config; diff --git a/packages/nocodb/src/plugins/ovhCloud/OvhCloud.ts b/packages/nocodb/src/plugins/ovhCloud/OvhCloud.ts index e2be101931..ceb09503cc 100644 --- a/packages/nocodb/src/plugins/ovhCloud/OvhCloud.ts +++ b/packages/nocodb/src/plugins/ovhCloud/OvhCloud.ts @@ -1,11 +1,10 @@ -import fs from "fs"; -import path from "path"; +import fs from 'fs'; +import path from 'path'; -import AWS from "aws-sdk"; -import {IStorageAdapter, XcFile} from "nc-plugin"; +import AWS from 'aws-sdk'; +import { IStorageAdapter, XcFile } from 'nc-plugin'; export default class OvhCloud implements IStorageAdapter { - private s3Client: AWS.S3; private input: any; @@ -13,16 +12,14 @@ export default class OvhCloud implements IStorageAdapter { this.input = input; } - async fileCreate(key: string, file: XcFile): Promise { - const uploadParams: any = { ACL: 'public-read' }; return new Promise((resolve, reject) => { // Configure the file stream and obtain the upload parameters const fileStream = fs.createReadStream(file.path); - fileStream.on('error', (err) => { + fileStream.on('error', err => { console.log('File Error', err); reject(err); }); @@ -33,7 +30,7 @@ export default class OvhCloud implements IStorageAdapter { // call S3 to retrieve upload file to specified bucket this.s3Client.upload(uploadParams, (err, data) => { if (err) { - console.log("Error", err); + console.log('Error', err); reject(err); } if (data) { @@ -49,7 +46,7 @@ export default class OvhCloud implements IStorageAdapter { public async fileRead(key: string): Promise { return new Promise((resolve, reject) => { - this.s3Client.getObject({Key: key} as any, (err, data) => { + this.s3Client.getObject({ Key: key } as any, (err, data) => { if (err) { return reject(err); } @@ -62,19 +59,19 @@ export default class OvhCloud implements IStorageAdapter { } public async init(): Promise { - const s3Options: any = { - params: {Bucket: this.input.bucket}, + params: { Bucket: this.input.bucket }, region: this.input.region }; - s3Options.accessKeyId = this.input.access_key + s3Options.accessKeyId = this.input.access_key; s3Options.secretAccessKey = this.input.access_secret; - s3Options.endpoint = new AWS.Endpoint(`s3.${this.input.region}.cloud.ovh.net`); + s3Options.endpoint = new AWS.Endpoint( + `s3.${this.input.region}.cloud.ovh.net` + ); this.s3Client = new AWS.S3(s3Options); - } public async test(): Promise { @@ -94,10 +91,8 @@ export default class OvhCloud implements IStorageAdapter { throw e; } } - } - /** * @copyright Copyright (c) 2021, Xgene Cloud Ltd * diff --git a/packages/nocodb/src/plugins/ovhCloud/OvhCloudPlugin.ts b/packages/nocodb/src/plugins/ovhCloud/OvhCloudPlugin.ts index 1b6bcba4e2..a214ecf6e3 100644 --- a/packages/nocodb/src/plugins/ovhCloud/OvhCloudPlugin.ts +++ b/packages/nocodb/src/plugins/ovhCloud/OvhCloudPlugin.ts @@ -1,13 +1,11 @@ -import {IStorageAdapter, XcStoragePlugin} from "nc-plugin"; - -import OvhCloud from "./OvhCloud"; +import { IStorageAdapter, XcStoragePlugin } from 'nc-plugin'; +import OvhCloud from './OvhCloud'; class OvhCloudPlugin extends XcStoragePlugin { - private static storageAdapter: OvhCloud; - public getAdapter(): IStorageAdapter { + public getAdapter(): IStorageAdapter { return OvhCloudPlugin.storageAdapter; } @@ -15,7 +13,6 @@ class OvhCloudPlugin extends XcStoragePlugin { OvhCloudPlugin.storageAdapter = new OvhCloud(config); await OvhCloudPlugin.storageAdapter.init(); } - } export default OvhCloudPlugin; diff --git a/packages/nocodb/src/plugins/ovhCloud/index.ts b/packages/nocodb/src/plugins/ovhCloud/index.ts index cc547a83f5..88c531e7d5 100644 --- a/packages/nocodb/src/plugins/ovhCloud/index.ts +++ b/packages/nocodb/src/plugins/ovhCloud/index.ts @@ -1,7 +1,7 @@ -import {XcActionType, XcType} from "nc-common"; -import {XcPluginConfig} from "nc-plugin"; +import { XcActionType, XcType } from 'nc-common'; +import { XcPluginConfig } from 'nc-plugin'; -import OvhCloud from "./OvhCloudPlugin"; +import OvhCloud from './OvhCloudPlugin'; const config: XcPluginConfig = { builder: OvhCloud, @@ -9,51 +9,61 @@ const config: XcPluginConfig = { version: '0.0.1', logo: 'plugins/ovhCloud.png', tags: 'Storage', - description: 'Upload your files to a space that you can access via HTTPS using the OpenStack Swift API, or the S3 API. ', + description: + 'Upload your files to a space that you can access via HTTPS using the OpenStack Swift API, or the S3 API. ', inputs: { title: 'Configure OvhCloud Object Storage', - items: [{ - key: 'bucket', - label: 'Bucket Name', - placeholder: 'Bucket Name', - type: XcType.SingleLineText, - required: true - }, { - key: 'region', - label: 'Region', - placeholder: 'Region', - type: XcType.SingleLineText, - required: true - }, { - key: 'access_key', - label: 'Access Key', - placeholder: 'Access Key', - type: XcType.SingleLineText, - required: true - }, { - key: 'access_secret', - label: 'Access Secret', - placeholder: 'Access Secret', - type: XcType.Password, - required: true - },], - actions: [{ - label: 'Test', - placeholder: 'Test', - key: 'test', - actionType: XcActionType.TEST, - type: XcType.Button - }, { - label: 'Save', - placeholder: 'Save', - key: 'save', - actionType: XcActionType.SUBMIT, - type: XcType.Button - },], - msgOnInstall: 'Successfully installed and attachment will be stored in OvhCloud Object Storage', - msgOnUninstall: '', + items: [ + { + key: 'bucket', + label: 'Bucket Name', + placeholder: 'Bucket Name', + type: XcType.SingleLineText, + required: true + }, + { + key: 'region', + label: 'Region', + placeholder: 'Region', + type: XcType.SingleLineText, + required: true + }, + { + key: 'access_key', + label: 'Access Key', + placeholder: 'Access Key', + type: XcType.SingleLineText, + required: true + }, + { + key: 'access_secret', + label: 'Access Secret', + placeholder: 'Access Secret', + type: XcType.Password, + required: true + } + ], + actions: [ + { + label: 'Test', + placeholder: 'Test', + key: 'test', + actionType: XcActionType.TEST, + type: XcType.Button + }, + { + label: 'Save', + placeholder: 'Save', + key: 'save', + actionType: XcActionType.SUBMIT, + type: XcType.Button + } + ], + msgOnInstall: + 'Successfully installed and attachment will be stored in OvhCloud Object Storage', + msgOnUninstall: '' }, - category: 'Storage', -} + category: 'Storage' +}; -export default config; \ No newline at end of file +export default config; diff --git a/packages/nocodb/src/plugins/s3/S3.ts b/packages/nocodb/src/plugins/s3/S3.ts index cb16ec5d20..a483861619 100644 --- a/packages/nocodb/src/plugins/s3/S3.ts +++ b/packages/nocodb/src/plugins/s3/S3.ts @@ -1,11 +1,10 @@ -import fs from "fs"; -import path from "path"; +import fs from 'fs'; +import path from 'path'; -import AWS from "aws-sdk"; -import {IStorageAdapter, XcFile} from "nc-plugin"; +import AWS from 'aws-sdk'; +import { IStorageAdapter, XcFile } from 'nc-plugin'; export default class S3 implements IStorageAdapter { - private s3Client: AWS.S3; private input: any; @@ -13,16 +12,14 @@ export default class S3 implements IStorageAdapter { this.input = input; } - async fileCreate(key: string, file: XcFile): Promise { - const uploadParams: any = { ACL: 'public-read' }; return new Promise((resolve, reject) => { // Configure the file stream and obtain the upload parameters const fileStream = fs.createReadStream(file.path); - fileStream.on('error', (err) => { + fileStream.on('error', err => { console.log('File Error', err); reject(err); }); @@ -33,7 +30,7 @@ export default class S3 implements IStorageAdapter { // call S3 to retrieve upload file to specified bucket this.s3Client.upload(uploadParams, (err, data) => { if (err) { - console.log("Error", err); + console.log('Error', err); reject(err); } if (data) { @@ -49,7 +46,7 @@ export default class S3 implements IStorageAdapter { public async fileRead(key: string): Promise { return new Promise((resolve, reject) => { - this.s3Client.getObject({Key: key} as any, (err, data) => { + this.s3Client.getObject({ Key: key } as any, (err, data) => { if (err) { return reject(err); } @@ -62,7 +59,6 @@ export default class S3 implements IStorageAdapter { } public async init(): Promise { - // const s3Options: any = { // params: {Bucket: process.env.NC_S3_BUCKET}, // region: process.env.NC_S3_REGION @@ -72,14 +68,13 @@ export default class S3 implements IStorageAdapter { // s3Options.secretAccessKey = process.env.NC_S3_SECRET; const s3Options: any = { - params: {Bucket: this.input.bucket}, + params: { Bucket: this.input.bucket }, region: this.input.region }; - s3Options.accessKeyId = this.input.access_key + s3Options.accessKeyId = this.input.access_key; s3Options.secretAccessKey = this.input.access_secret; - this.s3Client = new AWS.S3(s3Options); } @@ -96,14 +91,12 @@ export default class S3 implements IStorageAdapter { }); fs.unlinkSync(tempFile); return true; - }catch (e) { + } catch (e) { throw e; } } - } - /** * @copyright Copyright (c) 2021, Xgene Cloud Ltd * diff --git a/packages/nocodb/src/plugins/s3/S3Plugin.ts b/packages/nocodb/src/plugins/s3/S3Plugin.ts index 4b7a9674f0..f4719dd024 100644 --- a/packages/nocodb/src/plugins/s3/S3Plugin.ts +++ b/packages/nocodb/src/plugins/s3/S3Plugin.ts @@ -1,13 +1,11 @@ -import {IStorageAdapter, XcStoragePlugin} from "nc-plugin"; - -import S3 from "./S3"; +import { IStorageAdapter, XcStoragePlugin } from 'nc-plugin'; +import S3 from './S3'; class S3Plugin extends XcStoragePlugin { - private static storageAdapter: S3; - public getAdapter(): IStorageAdapter { + public getAdapter(): IStorageAdapter { return S3Plugin.storageAdapter; } @@ -15,7 +13,6 @@ class S3Plugin extends XcStoragePlugin { S3Plugin.storageAdapter = new S3(config); await S3Plugin.storageAdapter.init(); } - } export default S3Plugin; diff --git a/packages/nocodb/src/plugins/s3/index.ts b/packages/nocodb/src/plugins/s3/index.ts index 32eb64bafd..63855c7c41 100644 --- a/packages/nocodb/src/plugins/s3/index.ts +++ b/packages/nocodb/src/plugins/s3/index.ts @@ -1,59 +1,69 @@ -import {XcActionType, XcType} from "nc-common"; -import {XcPluginConfig} from "nc-plugin"; +import { XcActionType, XcType } from 'nc-common'; +import { XcPluginConfig } from 'nc-plugin'; -import S3Plugin from "./S3Plugin"; +import S3Plugin from './S3Plugin'; const config: XcPluginConfig = { builder: S3Plugin, title: 'S3', version: '0.0.1', logo: 'plugins/s3.png', - description: 'Amazon Simple Storage Service (Amazon S3) is an object storage service that offers industry-leading scalability, data availability, security, and performance.', + description: + 'Amazon Simple Storage Service (Amazon S3) is an object storage service that offers industry-leading scalability, data availability, security, and performance.', inputs: { title: 'Configure Amazon S3', - items: [{ - key: 'bucket', - label: 'Bucket Name', - placeholder: 'Bucket Name', - type: XcType.SingleLineText, - required: true - }, { - key: 'region', - label: 'Region', - placeholder: 'Region', - type: XcType.SingleLineText, - required: true - }, { - key: 'access_key', - label: 'Access Key', - placeholder: 'Access Key', - type: XcType.SingleLineText, - required: true - }, { - key: 'access_secret', - label: 'Access Secret', - placeholder: 'Access Secret', - type: XcType.Password, - required: true - },], - actions: [{ - label: 'Test', - placeholder: 'Test', - key: 'test', - actionType: XcActionType.TEST, - type: XcType.Button - }, { - label: 'Save', - placeholder: 'Save', - key: 'save', - actionType: XcActionType.SUBMIT, - type: XcType.Button - },], - msgOnInstall: 'Successfully installed and attachment will be stored in AWS S3', - msgOnUninstall: '', + items: [ + { + key: 'bucket', + label: 'Bucket Name', + placeholder: 'Bucket Name', + type: XcType.SingleLineText, + required: true + }, + { + key: 'region', + label: 'Region', + placeholder: 'Region', + type: XcType.SingleLineText, + required: true + }, + { + key: 'access_key', + label: 'Access Key', + placeholder: 'Access Key', + type: XcType.SingleLineText, + required: true + }, + { + key: 'access_secret', + label: 'Access Secret', + placeholder: 'Access Secret', + type: XcType.Password, + required: true + } + ], + actions: [ + { + label: 'Test', + placeholder: 'Test', + key: 'test', + actionType: XcActionType.TEST, + type: XcType.Button + }, + { + label: 'Save', + placeholder: 'Save', + key: 'save', + actionType: XcActionType.SUBMIT, + type: XcType.Button + } + ], + msgOnInstall: + 'Successfully installed and attachment will be stored in AWS S3', + msgOnUninstall: '' }, category: 'Storage', - tags: 'Storage', -} + tags: 'Storage' +}; -export default config; \ No newline at end of file +export default config; diff --git a/packages/nocodb/src/plugins/scaleway/ScalewayObjectStorage.ts b/packages/nocodb/src/plugins/scaleway/ScalewayObjectStorage.ts index 545e00a08f..06627427a8 100644 --- a/packages/nocodb/src/plugins/scaleway/ScalewayObjectStorage.ts +++ b/packages/nocodb/src/plugins/scaleway/ScalewayObjectStorage.ts @@ -1,11 +1,10 @@ -import fs from "fs"; -import path from "path"; +import fs from 'fs'; +import path from 'path'; -import AWS from "aws-sdk"; -import {IStorageAdapter, XcFile} from "nc-plugin"; +import AWS from 'aws-sdk'; +import { IStorageAdapter, XcFile } from 'nc-plugin'; export default class ScalewayObjectStorage implements IStorageAdapter { - private s3Client: AWS.S3; private input: any; @@ -13,16 +12,14 @@ export default class ScalewayObjectStorage implements IStorageAdapter { this.input = input; } - async fileCreate(key: string, file: XcFile): Promise { - const uploadParams: any = { ACL: 'public-read' }; return new Promise((resolve, reject) => { // Configure the file stream and obtain the upload parameters const fileStream = fs.createReadStream(file.path); - fileStream.on('error', (err) => { + fileStream.on('error', err => { console.log('File Error', err); reject(err); }); @@ -33,7 +30,7 @@ export default class ScalewayObjectStorage implements IStorageAdapter { // call S3 to retrieve upload file to specified bucket this.s3Client.upload(uploadParams, (err, data) => { if (err) { - console.log("Error", err); + console.log('Error', err); reject(err); } if (data) { @@ -49,7 +46,7 @@ export default class ScalewayObjectStorage implements IStorageAdapter { public async fileRead(key: string): Promise { return new Promise((resolve, reject) => { - this.s3Client.getObject({Key: key} as any, (err, data) => { + this.s3Client.getObject({ Key: key } as any, (err, data) => { if (err) { return reject(err); } @@ -63,11 +60,11 @@ export default class ScalewayObjectStorage implements IStorageAdapter { public async init(): Promise { const s3Options: any = { - params: {Bucket: this.input.bucket}, + params: { Bucket: this.input.bucket }, region: this.input.region }; - s3Options.accessKeyId = this.input.access_key + s3Options.accessKeyId = this.input.access_key; s3Options.secretAccessKey = this.input.access_secret; s3Options.endpoint = new AWS.Endpoint(`s3.${this.input.region}.scw.cloud`); @@ -92,10 +89,8 @@ export default class ScalewayObjectStorage implements IStorageAdapter { throw e; } } - } - /** * @copyright Copyright (c) 2021, Bhanu P Chaudhary * @@ -116,4 +111,4 @@ export default class ScalewayObjectStorage implements IStorageAdapter { * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . * - */ \ No newline at end of file + */ diff --git a/packages/nocodb/src/plugins/scaleway/ScalewayObjectStoragePlugin.ts b/packages/nocodb/src/plugins/scaleway/ScalewayObjectStoragePlugin.ts index b7f1f3f74e..1a09c8d655 100644 --- a/packages/nocodb/src/plugins/scaleway/ScalewayObjectStoragePlugin.ts +++ b/packages/nocodb/src/plugins/scaleway/ScalewayObjectStoragePlugin.ts @@ -1,26 +1,24 @@ -import {IStorageAdapter, XcStoragePlugin} from "nc-plugin"; - -import ScalewayObjectStorage from "./ScalewayObjectStorage"; +import { IStorageAdapter, XcStoragePlugin } from 'nc-plugin'; +import ScalewayObjectStorage from './ScalewayObjectStorage'; class ScalewayObjectStoragePlugin extends XcStoragePlugin { - private static storageAdapter: ScalewayObjectStorage; - public getAdapter(): IStorageAdapter { + public getAdapter(): IStorageAdapter { return ScalewayObjectStoragePlugin.storageAdapter; } public async init(config: any): Promise { - ScalewayObjectStoragePlugin.storageAdapter = new ScalewayObjectStorage(config); + ScalewayObjectStoragePlugin.storageAdapter = new ScalewayObjectStorage( + config + ); await ScalewayObjectStoragePlugin.storageAdapter.init(); } - } export default ScalewayObjectStoragePlugin; - /** * @copyright Copyright (c) 2021, Bhanu P Chaudhary * @@ -41,4 +39,4 @@ export default ScalewayObjectStoragePlugin; * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . * - */ \ No newline at end of file + */ diff --git a/packages/nocodb/src/plugins/scaleway/index.ts b/packages/nocodb/src/plugins/scaleway/index.ts index b9130ae2d2..797bcb2fc6 100644 --- a/packages/nocodb/src/plugins/scaleway/index.ts +++ b/packages/nocodb/src/plugins/scaleway/index.ts @@ -1,7 +1,7 @@ -import {XcActionType, XcType} from "nc-common"; -import {XcPluginConfig} from "nc-plugin"; +import { XcActionType, XcType } from 'nc-common'; +import { XcPluginConfig } from 'nc-plugin'; -import ScalewayObjectStoragePlugin from "./ScalewayObjectStoragePlugin"; +import ScalewayObjectStoragePlugin from './ScalewayObjectStoragePlugin'; const config: XcPluginConfig = { builder: ScalewayObjectStoragePlugin, @@ -9,52 +9,62 @@ const config: XcPluginConfig = { version: '0.0.1', logo: 'plugins/scaleway.png', tags: 'Storage', - description: 'S3-compatible Scaleway Object Storage makes it easy and more affordable to store and access data on Scaleway Cloud Platform infrastructure. The service also gives a 75GB free storage and external outgoing transfer on Object Storage every month', + description: + 'S3-compatible Scaleway Object Storage makes it easy and more affordable to store and access data on Scaleway Cloud Platform infrastructure. The service also gives a 75GB free storage and external outgoing transfer on Object Storage every month', inputs: { title: 'Configure Scaleway Object Storage', - items: [{ - key: 'bucket', - label: 'Bucket Name', - placeholder: 'Bucket Name', - type: XcType.SingleLineText, - required: true - }, { - key: 'region', - label: 'Region', - placeholder: 'Region', - type: XcType.SingleLineText, - required: true - }, { - key: 'access_key', - label: 'Access Key', - placeholder: 'Access Key', - type: XcType.SingleLineText, - required: true - }, { - key: 'access_secret', - label: 'Access Secret', - placeholder: 'Access Secret', - type: XcType.Password, - required: true - },], - actions: [{ - label: 'Test', - placeholder: 'Test', - key: 'test', - actionType: XcActionType.TEST, - type: XcType.Button - }, { - label: 'Save', - placeholder: 'Save', - key: 'save', - actionType: XcActionType.SUBMIT, - type: XcType.Button - },], - msgOnInstall: 'Successfully installed and attachment will be stored in Scaleway Object Storage', - msgOnUninstall: '', + items: [ + { + key: 'bucket', + label: 'Bucket Name', + placeholder: 'Bucket Name', + type: XcType.SingleLineText, + required: true + }, + { + key: 'region', + label: 'Region', + placeholder: 'Region', + type: XcType.SingleLineText, + required: true + }, + { + key: 'access_key', + label: 'Access Key', + placeholder: 'Access Key', + type: XcType.SingleLineText, + required: true + }, + { + key: 'access_secret', + label: 'Access Secret', + placeholder: 'Access Secret', + type: XcType.Password, + required: true + } + ], + actions: [ + { + label: 'Test', + placeholder: 'Test', + key: 'test', + actionType: XcActionType.TEST, + type: XcType.Button + }, + { + label: 'Save', + placeholder: 'Save', + key: 'save', + actionType: XcActionType.SUBMIT, + type: XcType.Button + } + ], + msgOnInstall: + 'Successfully installed and attachment will be stored in Scaleway Object Storage', + msgOnUninstall: '' }, - category: 'Storage', -} + category: 'Storage' +}; export default config; @@ -78,4 +88,4 @@ export default config; * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . * - */ \ No newline at end of file + */ diff --git a/packages/nocodb/src/plugins/slack/Slack.ts b/packages/nocodb/src/plugins/slack/Slack.ts index 769e687689..15e0822af7 100644 --- a/packages/nocodb/src/plugins/slack/Slack.ts +++ b/packages/nocodb/src/plugins/slack/Slack.ts @@ -1,6 +1,5 @@ -import axios from "axios"; -import {IWebhookNotificationAdapter} from "nc-plugin"; - +import axios from 'axios'; +import { IWebhookNotificationAdapter } from 'nc-plugin'; export default class Slack implements IWebhookNotificationAdapter { public init(): Promise { @@ -8,21 +7,18 @@ export default class Slack implements IWebhookNotificationAdapter { } public async sendMessage(text: string, payload: any): Promise { - for (const {webhook_url} of payload?.channels) { + for (const { webhook_url } of payload?.channels) { try { await axios.post(webhook_url, { text - }) + }); } catch (e) { - console.log(e) + console.log(e); } } } - - } - /** * @copyright Copyright (c) 2021, Xgene Cloud Ltd * diff --git a/packages/nocodb/src/plugins/slack/SlackPlugin.ts b/packages/nocodb/src/plugins/slack/SlackPlugin.ts index 582a0d45f2..b010ed73cd 100644 --- a/packages/nocodb/src/plugins/slack/SlackPlugin.ts +++ b/packages/nocodb/src/plugins/slack/SlackPlugin.ts @@ -1,13 +1,14 @@ -import {IWebhookNotificationAdapter, XcWebhookNotificationPlugin} from "nc-plugin"; - -import Slack from "./Slack"; +import { + IWebhookNotificationAdapter, + XcWebhookNotificationPlugin +} from 'nc-plugin'; +import Slack from './Slack'; class SlackPlugin extends XcWebhookNotificationPlugin { - private static notificationAdapter: Slack; - public getAdapter(): IWebhookNotificationAdapter { + public getAdapter(): IWebhookNotificationAdapter { return SlackPlugin.notificationAdapter; } @@ -15,7 +16,6 @@ class SlackPlugin extends XcWebhookNotificationPlugin { SlackPlugin.notificationAdapter = new Slack(); await SlackPlugin.notificationAdapter.init(); } - } export default SlackPlugin; diff --git a/packages/nocodb/src/plugins/slack/index.ts b/packages/nocodb/src/plugins/slack/index.ts index e1350579fc..eb0cf231f5 100644 --- a/packages/nocodb/src/plugins/slack/index.ts +++ b/packages/nocodb/src/plugins/slack/index.ts @@ -1,49 +1,57 @@ -import {XcActionType, XcType} from "nc-common"; -import {XcPluginConfig} from "nc-plugin"; +import { XcActionType, XcType } from 'nc-common'; +import { XcPluginConfig } from 'nc-plugin'; -import SlackPlugin from "./SlackPlugin"; +import SlackPlugin from './SlackPlugin'; -const config: XcPluginConfig ={ +const config: XcPluginConfig = { builder: SlackPlugin, title: 'Slack', version: '0.0.1', logo: 'plugins/slack.webp', - description: 'Slack brings team communication and collaboration into one place so you can get more work done, whether you belong to a large enterprise or a small business. ', + description: + 'Slack brings team communication and collaboration into one place so you can get more work done, whether you belong to a large enterprise or a small business. ', price: 'Free', tags: 'Chat', category: 'Chat', - inputs:{ + inputs: { title: 'Configure Slack', array: true, - items: [{ - key: 'channel', - label: 'Channel Name', - placeholder: 'Channel Name', - type: XcType.SingleLineText, - required: true - }, { - key: 'webhook_url', - label: 'Webhook URL', - placeholder: 'Webhook URL', - type: XcType.Password, - required: true - },], - actions: [{ - label: 'Test', - placeholder: 'Test', - key: 'test', - actionType: XcActionType.TEST, - type: XcType.Button - }, { - label: 'Save', - placeholder: 'Save', - key: 'save', - actionType: XcActionType.SUBMIT, - type: XcType.Button - },], - msgOnInstall: 'Successfully installed and Slack is enabled for notification.', - msgOnUninstall: '', + items: [ + { + key: 'channel', + label: 'Channel Name', + placeholder: 'Channel Name', + type: XcType.SingleLineText, + required: true + }, + { + key: 'webhook_url', + label: 'Webhook URL', + placeholder: 'Webhook URL', + type: XcType.Password, + required: true + } + ], + actions: [ + { + label: 'Test', + placeholder: 'Test', + key: 'test', + actionType: XcActionType.TEST, + type: XcType.Button + }, + { + label: 'Save', + placeholder: 'Save', + key: 'save', + actionType: XcActionType.SUBMIT, + type: XcType.Button + } + ], + msgOnInstall: + 'Successfully installed and Slack is enabled for notification.', + msgOnUninstall: '' } -} +}; -export default config; \ No newline at end of file +export default config; diff --git a/packages/nocodb/src/plugins/smtp/SMTP.ts b/packages/nocodb/src/plugins/smtp/SMTP.ts index 93752125cb..ecaee629c7 100644 --- a/packages/nocodb/src/plugins/smtp/SMTP.ts +++ b/packages/nocodb/src/plugins/smtp/SMTP.ts @@ -1,11 +1,10 @@ -import {IEmailAdapter} from "nc-plugin"; -import nodemailer from "nodemailer"; -import Mail from "nodemailer/lib/mailer"; +import { IEmailAdapter } from 'nc-plugin'; +import nodemailer from 'nodemailer'; +import Mail from 'nodemailer/lib/mailer'; -import {XcEmail} from "../../interface/IEmailAdapter"; +import { XcEmail } from '../../interface/IEmailAdapter'; export default class SMTP implements IEmailAdapter { - private transporter: Mail; private input: any; @@ -17,41 +16,39 @@ export default class SMTP implements IEmailAdapter { const config = { // from: this.input.from, // options: { - "host": this.input?.host, - "port": parseInt(this.input?.port, 10), - "secure": this.input?.secure === 'true', - "ignoreTLS": this.input?.ignoreTLS === 'true', - "auth": { - "user": this.input?.username, - "pass": this.input?.password + host: this.input?.host, + port: parseInt(this.input?.port, 10), + secure: this.input?.secure === 'true', + ignoreTLS: this.input?.ignoreTLS === 'true', + auth: { + user: this.input?.username, + pass: this.input?.password } // } - } + }; this.transporter = nodemailer.createTransport(config); } public async mailSend(mail: XcEmail): Promise { if (this.transporter) { - await this.transporter.sendMail({...mail, from: this.input.from}) + await this.transporter.sendMail({ ...mail, from: this.input.from }); } } public async test(email): Promise { try { - await this.mailSend({ + await this.mailSend({ to: email, - subject: "Test email", + subject: 'Test email', html: 'Test email' - } as any) + } as any); return true; } catch (e) { throw e; } } - } - /** * @copyright Copyright (c) 2021, Xgene Cloud Ltd * diff --git a/packages/nocodb/src/plugins/smtp/SMTPPlugin.ts b/packages/nocodb/src/plugins/smtp/SMTPPlugin.ts index 534497d975..ba837be9b3 100644 --- a/packages/nocodb/src/plugins/smtp/SMTPPlugin.ts +++ b/packages/nocodb/src/plugins/smtp/SMTPPlugin.ts @@ -1,13 +1,11 @@ -import {IEmailAdapter, XcEmailPlugin} from "nc-plugin"; - -import SMTP from "./SMTP"; +import { IEmailAdapter, XcEmailPlugin } from 'nc-plugin'; +import SMTP from './SMTP'; class SMTPPlugin extends XcEmailPlugin { - private static storageAdapter: SMTP; - public getAdapter(): IEmailAdapter { + public getAdapter(): IEmailAdapter { return SMTPPlugin.storageAdapter; } @@ -15,7 +13,6 @@ class SMTPPlugin extends XcEmailPlugin { SMTPPlugin.storageAdapter = new SMTP(config); await SMTPPlugin.storageAdapter.init(); } - } export default SMTPPlugin; diff --git a/packages/nocodb/src/plugins/smtp/index.ts b/packages/nocodb/src/plugins/smtp/index.ts index 83e1d59a3e..b625f5a329 100644 --- a/packages/nocodb/src/plugins/smtp/index.ts +++ b/packages/nocodb/src/plugins/smtp/index.ts @@ -1,11 +1,11 @@ -import {XcActionType, XcType} from "nc-common"; -import {XcPluginConfig} from "nc-plugin"; +import { XcActionType, XcType } from 'nc-common'; +import { XcPluginConfig } from 'nc-plugin'; -import SMTPPlugin from "./SMTPPlugin"; +import SMTPPlugin from './SMTPPlugin'; // @author -const config: XcPluginConfig ={ +const config: XcPluginConfig = { builder: SMTPPlugin, title: 'SMTP', version: '0.0.1', @@ -14,65 +14,77 @@ const config: XcPluginConfig ={ price: 'Free', tags: 'Email', category: 'Email', - inputs:{ + inputs: { title: 'Configure Email SMTP', - items: [{ - key: 'from', - label: 'From', - placeholder: 'eg: admin@example.com', - type: XcType.SingleLineText, - required: true - }, { - key: 'host', - label: 'Host', - placeholder: 'eg: smtp.example.com', - type: XcType.SingleLineText, - required: true - }, { - key: 'port', - label: 'Port', - placeholder: 'Port', - type: XcType.SingleLineText, - required: true - }, { - key: 'secure', - label: 'Secure', - placeholder: 'Secure', - type: XcType.SingleLineText, - required: true - }, { - key: 'ignoreTLS', - label: 'IgnoreTLS', - placeholder: 'IgnoreTLS', - type: XcType.SingleLineText, - required: false - }, { + items: [ + { + key: 'from', + label: 'From', + placeholder: 'eg: admin@example.com', + type: XcType.SingleLineText, + required: true + }, + { + key: 'host', + label: 'Host', + placeholder: 'eg: smtp.example.com', + type: XcType.SingleLineText, + required: true + }, + { + key: 'port', + label: 'Port', + placeholder: 'Port', + type: XcType.SingleLineText, + required: true + }, + { + key: 'secure', + label: 'Secure', + placeholder: 'Secure', + type: XcType.SingleLineText, + required: true + }, + { + key: 'ignoreTLS', + label: 'IgnoreTLS', + placeholder: 'IgnoreTLS', + type: XcType.SingleLineText, + required: false + }, + { key: 'username', - label: 'Username', - placeholder: 'Username', - type: XcType.SingleLineText, - required: false - }, { - key: 'password', - label: 'Password', - placeholder: 'Password', - type: XcType.Password, - required: false - },], - actions: [{ - label: 'Test', - key: 'test', - actionType: XcActionType.TEST, - type: XcType.Button - }, { - label: 'Save', - key: 'save', - actionType: XcActionType.SUBMIT, - type: XcType.Button - },], - msgOnInstall: 'Successfully installed and email notification will use SMTP configuration', - msgOnUninstall: '', + label: 'Username', + placeholder: 'Username', + type: XcType.SingleLineText, + required: false + }, + { + key: 'password', + label: 'Password', + placeholder: 'Password', + type: XcType.Password, + required: false + } + ], + actions: [ + { + label: 'Test', + key: 'test', + actionType: XcActionType.TEST, + type: XcType.Button + }, + { + label: 'Save', + key: 'save', + actionType: XcActionType.SUBMIT, + type: XcType.Button + } + ], + msgOnInstall: + 'Successfully installed and email notification will use SMTP configuration', + msgOnUninstall: '' } -} +}; -export default config; \ No newline at end of file +export default config; diff --git a/packages/nocodb/src/plugins/spaces/Spaces.ts b/packages/nocodb/src/plugins/spaces/Spaces.ts index ede70ea965..5086871f47 100644 --- a/packages/nocodb/src/plugins/spaces/Spaces.ts +++ b/packages/nocodb/src/plugins/spaces/Spaces.ts @@ -1,11 +1,10 @@ -import fs from "fs"; -import path from "path"; +import fs from 'fs'; +import path from 'path'; -import AWS from "aws-sdk"; -import {IStorageAdapter, XcFile} from "nc-plugin"; +import AWS from 'aws-sdk'; +import { IStorageAdapter, XcFile } from 'nc-plugin'; export default class Spaces implements IStorageAdapter { - private s3Client: AWS.S3; private input: any; @@ -13,16 +12,14 @@ export default class Spaces implements IStorageAdapter { this.input = input; } - async fileCreate(key: string, file: XcFile): Promise { - const uploadParams: any = { ACL: 'public-read' }; return new Promise((resolve, reject) => { // Configure the file stream and obtain the upload parameters const fileStream = fs.createReadStream(file.path); - fileStream.on('error', (err) => { + fileStream.on('error', err => { console.log('File Error', err); reject(err); }); @@ -33,7 +30,7 @@ export default class Spaces implements IStorageAdapter { // call S3 to retrieve upload file to specified bucket this.s3Client.upload(uploadParams, (err, data) => { if (err) { - console.log("Error", err); + console.log('Error', err); reject(err); } if (data) { @@ -49,7 +46,7 @@ export default class Spaces implements IStorageAdapter { public async fileRead(key: string): Promise { return new Promise((resolve, reject) => { - this.s3Client.getObject({Key: key} as any, (err, data) => { + this.s3Client.getObject({ Key: key } as any, (err, data) => { if (err) { return reject(err); } @@ -62,7 +59,6 @@ export default class Spaces implements IStorageAdapter { } public async init(): Promise { - // const s3Options: any = { // params: {Bucket: process.env.NC_S3_BUCKET}, // region: process.env.NC_S3_REGION @@ -72,14 +68,16 @@ export default class Spaces implements IStorageAdapter { // s3Options.secretAccessKey = process.env.NC_S3_SECRET; const s3Options: any = { - params: {Bucket: this.input.bucket}, + params: { Bucket: this.input.bucket }, region: this.input.region }; - s3Options.accessKeyId = this.input.access_key + s3Options.accessKeyId = this.input.access_key; s3Options.secretAccessKey = this.input.access_secret; - s3Options.endpoint = new AWS.Endpoint(`${this.input.region || 'nyc3'}.digitaloceanspaces.com`); + s3Options.endpoint = new AWS.Endpoint( + `${this.input.region || 'nyc3'}.digitaloceanspaces.com` + ); this.s3Client = new AWS.S3(s3Options); } @@ -101,10 +99,8 @@ export default class Spaces implements IStorageAdapter { throw e; } } - } - /** * @copyright Copyright (c) 2021, Xgene Cloud Ltd * diff --git a/packages/nocodb/src/plugins/spaces/SpacesPlugin.ts b/packages/nocodb/src/plugins/spaces/SpacesPlugin.ts index 34dfe5ef1f..5b1fce3455 100644 --- a/packages/nocodb/src/plugins/spaces/SpacesPlugin.ts +++ b/packages/nocodb/src/plugins/spaces/SpacesPlugin.ts @@ -1,13 +1,11 @@ -import {IStorageAdapter, XcStoragePlugin} from "nc-plugin"; - -import Spaces from "./Spaces"; +import { IStorageAdapter, XcStoragePlugin } from 'nc-plugin'; +import Spaces from './Spaces'; class SpacesPlugin extends XcStoragePlugin { - private static storageAdapter: Spaces; - public getAdapter(): IStorageAdapter { + public getAdapter(): IStorageAdapter { return SpacesPlugin.storageAdapter; } @@ -15,7 +13,6 @@ class SpacesPlugin extends XcStoragePlugin { SpacesPlugin.storageAdapter = new Spaces(config); await SpacesPlugin.storageAdapter.init(); } - } export default SpacesPlugin; diff --git a/packages/nocodb/src/plugins/spaces/index.ts b/packages/nocodb/src/plugins/spaces/index.ts index d627bbba71..bf88440e35 100644 --- a/packages/nocodb/src/plugins/spaces/index.ts +++ b/packages/nocodb/src/plugins/spaces/index.ts @@ -1,60 +1,70 @@ -import {XcActionType, XcType} from "nc-common"; -import {XcPluginConfig} from "nc-plugin"; +import { XcActionType, XcType } from 'nc-common'; +import { XcPluginConfig } from 'nc-plugin'; -import SpacesPlugin from "./SpacesPlugin"; +import SpacesPlugin from './SpacesPlugin'; -const config: XcPluginConfig ={ +const config: XcPluginConfig = { builder: SpacesPlugin, title: 'Spaces', version: '0.0.1', logo: 'plugins/spaces.png', - description: 'Store & deliver vast amounts of content with a simple architecture.', + description: + 'Store & deliver vast amounts of content with a simple architecture.', price: 'Free', tags: 'Storage', category: 'Storage', - inputs:{ + inputs: { title: 'DigitalOcean Spaces', - items: [{ - key: 'bucket', - label: 'Bucket Name', - placeholder: 'Bucket Name', - type: XcType.SingleLineText, - required: true - }, { - key: 'region', - label: 'Region', - placeholder: 'Region', - type: XcType.SingleLineText, - required: true - }, { - key: 'access_key', - label: 'Access Key', - placeholder: 'Access Key', - type: XcType.SingleLineText, - required: true - }, { - key: 'access_secret', - label: 'Access Secret', - placeholder: 'Access Secret', - type: XcType.Password, - required: true - },], - actions: [{ - label: 'Test', - placeholder: 'Test', - key: 'test', - actionType: XcActionType.TEST, - type: XcType.Button - }, { - label: 'Save', - placeholder: 'Save', - key: 'save', - actionType: XcActionType.SUBMIT, - type: XcType.Button - },], - msgOnInstall:'Successfully installed and attachment will be stored in DigitalOcean Spaces', - msgOnUninstall:'', + items: [ + { + key: 'bucket', + label: 'Bucket Name', + placeholder: 'Bucket Name', + type: XcType.SingleLineText, + required: true + }, + { + key: 'region', + label: 'Region', + placeholder: 'Region', + type: XcType.SingleLineText, + required: true + }, + { + key: 'access_key', + label: 'Access Key', + placeholder: 'Access Key', + type: XcType.SingleLineText, + required: true + }, + { + key: 'access_secret', + label: 'Access Secret', + placeholder: 'Access Secret', + type: XcType.Password, + required: true + } + ], + actions: [ + { + label: 'Test', + placeholder: 'Test', + key: 'test', + actionType: XcActionType.TEST, + type: XcType.Button + }, + { + label: 'Save', + placeholder: 'Save', + key: 'save', + actionType: XcActionType.SUBMIT, + type: XcType.Button + } + ], + msgOnInstall: + 'Successfully installed and attachment will be stored in DigitalOcean Spaces', + msgOnUninstall: '' } -} +}; -export default config; \ No newline at end of file +export default config; diff --git a/packages/nocodb/src/plugins/teams/Teams.ts b/packages/nocodb/src/plugins/teams/Teams.ts index 5e42f6d6b1..63eca55b5b 100644 --- a/packages/nocodb/src/plugins/teams/Teams.ts +++ b/packages/nocodb/src/plugins/teams/Teams.ts @@ -1,6 +1,5 @@ -import axios from "axios"; -import {IWebhookNotificationAdapter} from "nc-plugin"; - +import axios from 'axios'; +import { IWebhookNotificationAdapter } from 'nc-plugin'; export default class Teams implements IWebhookNotificationAdapter { public init(): Promise { @@ -8,21 +7,18 @@ export default class Teams implements IWebhookNotificationAdapter { } public async sendMessage(Text: string, payload: any): Promise { - for (const {webhook_url} of payload?.channels) { + for (const { webhook_url } of payload?.channels) { try { await axios.post(webhook_url, { Text - }) + }); } catch (e) { - console.log(e) + console.log(e); } } } - - } - /** * @copyright Copyright (c) 2021, Xgene Cloud Ltd * diff --git a/packages/nocodb/src/plugins/teams/TeamsPlugin.ts b/packages/nocodb/src/plugins/teams/TeamsPlugin.ts index a7fda6ffa5..da6f840829 100644 --- a/packages/nocodb/src/plugins/teams/TeamsPlugin.ts +++ b/packages/nocodb/src/plugins/teams/TeamsPlugin.ts @@ -1,13 +1,14 @@ -import {IWebhookNotificationAdapter, XcWebhookNotificationPlugin} from "nc-plugin"; - -import Teams from "./Teams"; +import { + IWebhookNotificationAdapter, + XcWebhookNotificationPlugin +} from 'nc-plugin'; +import Teams from './Teams'; class TeamsPlugin extends XcWebhookNotificationPlugin { - private static notificationAdapter: Teams; - public getAdapter(): IWebhookNotificationAdapter { + public getAdapter(): IWebhookNotificationAdapter { return TeamsPlugin.notificationAdapter; } @@ -15,7 +16,6 @@ class TeamsPlugin extends XcWebhookNotificationPlugin { TeamsPlugin.notificationAdapter = new Teams(); await TeamsPlugin.notificationAdapter.init(); } - } export default TeamsPlugin; diff --git a/packages/nocodb/src/plugins/teams/index.ts b/packages/nocodb/src/plugins/teams/index.ts index 761055a11e..69a103a369 100644 --- a/packages/nocodb/src/plugins/teams/index.ts +++ b/packages/nocodb/src/plugins/teams/index.ts @@ -1,49 +1,57 @@ -import {XcActionType, XcType} from "nc-common"; -import {XcPluginConfig} from "nc-plugin"; +import { XcActionType, XcType } from 'nc-common'; +import { XcPluginConfig } from 'nc-plugin'; -import TeamsPlugin from "./TeamsPlugin"; +import TeamsPlugin from './TeamsPlugin'; -const config: XcPluginConfig ={ +const config: XcPluginConfig = { builder: TeamsPlugin, title: 'Microsoft Teams', version: '0.0.1', logo: 'plugins/teams.ico', - description: 'Microsoft Teams is for everyone · Instantly go from group chat to video call with the touch of a button.', + description: + 'Microsoft Teams is for everyone · Instantly go from group chat to video call with the touch of a button.', price: 'Free', tags: 'Chat', category: 'Chat', - inputs:{ + inputs: { title: 'Configure Microsoft Teams', array: true, - items: [{ - key: 'channel', - label: 'Channel Name', - placeholder: 'Channel Name', - type: XcType.SingleLineText, - required: true - }, { - key: 'webhook_url', - label: 'Webhook URL', - placeholder: 'Webhook URL', - type: XcType.Password, - required: true - },], - actions: [{ - label: 'Test', - placeholder: 'Test', - key: 'test', - actionType: XcActionType.TEST, - type: XcType.Button - }, { - label: 'Save', - placeholder: 'Save', - key: 'save', - actionType: XcActionType.SUBMIT, - type: XcType.Button - },], - msgOnInstall: 'Successfully installed and Microsoft Teams is enabled for notification.', - msgOnUninstall: '', + items: [ + { + key: 'channel', + label: 'Channel Name', + placeholder: 'Channel Name', + type: XcType.SingleLineText, + required: true + }, + { + key: 'webhook_url', + label: 'Webhook URL', + placeholder: 'Webhook URL', + type: XcType.Password, + required: true + } + ], + actions: [ + { + label: 'Test', + placeholder: 'Test', + key: 'test', + actionType: XcActionType.TEST, + type: XcType.Button + }, + { + label: 'Save', + placeholder: 'Save', + key: 'save', + actionType: XcActionType.SUBMIT, + type: XcType.Button + } + ], + msgOnInstall: + 'Successfully installed and Microsoft Teams is enabled for notification.', + msgOnUninstall: '' } -} +}; -export default config; \ No newline at end of file +export default config; diff --git a/packages/nocodb/src/plugins/twilio/Twilio.ts b/packages/nocodb/src/plugins/twilio/Twilio.ts index d63272d5a1..81dcef3c50 100644 --- a/packages/nocodb/src/plugins/twilio/Twilio.ts +++ b/packages/nocodb/src/plugins/twilio/Twilio.ts @@ -1,6 +1,5 @@ -import {IWebhookNotificationAdapter} from "nc-plugin"; -import twilio from "twilio"; - +import { IWebhookNotificationAdapter } from 'nc-plugin'; +import twilio from 'twilio'; export default class Twilio implements IWebhookNotificationAdapter { private input: any; @@ -17,22 +16,18 @@ export default class Twilio implements IWebhookNotificationAdapter { public async sendMessage(content: string, payload: any): Promise { for (const num of payload?.to?.split(/\s*?,\s*?/)) { try { - await this.client.messages - .create({ - body: content, - from: this.input.from, - to: num - }) + await this.client.messages.create({ + body: content, + from: this.input.from, + to: num + }); } catch (e) { - console.log(e) + console.log(e); } } } - - } - /** * @copyright Copyright (c) 2021, Xgene Cloud Ltd * diff --git a/packages/nocodb/src/plugins/twilio/TwilioPlugin.ts b/packages/nocodb/src/plugins/twilio/TwilioPlugin.ts index dfd184ebd4..296b29bade 100644 --- a/packages/nocodb/src/plugins/twilio/TwilioPlugin.ts +++ b/packages/nocodb/src/plugins/twilio/TwilioPlugin.ts @@ -1,13 +1,14 @@ -import {IWebhookNotificationAdapter, XcWebhookNotificationPlugin} from "nc-plugin"; - -import Twilio from "./Twilio"; +import { + IWebhookNotificationAdapter, + XcWebhookNotificationPlugin +} from 'nc-plugin'; +import Twilio from './Twilio'; class TwilioPlugin extends XcWebhookNotificationPlugin { - private static notificationAdapter: Twilio; - public getAdapter(): IWebhookNotificationAdapter { + public getAdapter(): IWebhookNotificationAdapter { return TwilioPlugin.notificationAdapter; } @@ -15,7 +16,6 @@ class TwilioPlugin extends XcWebhookNotificationPlugin { TwilioPlugin.notificationAdapter = new Twilio(config); await TwilioPlugin.notificationAdapter.init(); } - } export default TwilioPlugin; diff --git a/packages/nocodb/src/plugins/twilio/index.ts b/packages/nocodb/src/plugins/twilio/index.ts index 669389efb8..2d5d14bf22 100644 --- a/packages/nocodb/src/plugins/twilio/index.ts +++ b/packages/nocodb/src/plugins/twilio/index.ts @@ -1,54 +1,63 @@ -import {XcActionType, XcType} from "nc-common"; -import {XcPluginConfig} from "nc-plugin"; +import { XcActionType, XcType } from 'nc-common'; +import { XcPluginConfig } from 'nc-plugin'; -import TwilioPlugin from "./TwilioPlugin"; +import TwilioPlugin from './TwilioPlugin'; -const config: XcPluginConfig ={ +const config: XcPluginConfig = { builder: TwilioPlugin, title: 'Twilio', version: '0.0.1', logo: 'plugins/twilio.png', - description: 'With Twilio, unite communications and strengthen customer relationships across your business – from marketing and sales to customer service and operations.', + description: + 'With Twilio, unite communications and strengthen customer relationships across your business – from marketing and sales to customer service and operations.', price: 'Free', tags: 'Chat', category: 'Twilio', - inputs:{ + inputs: { title: 'Configure Twilio', - items: [{ - key: 'sid', - label: 'Account SID', - placeholder: 'Account SID', - type: XcType.SingleLineText, - required: true - }, { - key: 'token', - label: 'Auth Token' , - placeholder: 'Auth Token', - type: XcType.Password, - required: true - }, { - key: 'from', - label: 'From Phone Number', - placeholder: 'From Phone Number', - type: XcType.SingleLineText, - required: true - },], - actions: [{ - label: 'Test', - placeholder: 'Test', - key: 'test', - actionType: XcActionType.TEST, - type: XcType.Button - }, { - label: 'Save', - placeholder: 'Save', - key: 'save', - actionType: XcActionType.SUBMIT, - type: XcType.Button - },], - msgOnInstall: 'Successfully installed and Twilio is enabled for notification.', - msgOnUninstall: '', + items: [ + { + key: 'sid', + label: 'Account SID', + placeholder: 'Account SID', + type: XcType.SingleLineText, + required: true + }, + { + key: 'token', + label: 'Auth Token', + placeholder: 'Auth Token', + type: XcType.Password, + required: true + }, + { + key: 'from', + label: 'From Phone Number', + placeholder: 'From Phone Number', + type: XcType.SingleLineText, + required: true + } + ], + actions: [ + { + label: 'Test', + placeholder: 'Test', + key: 'test', + actionType: XcActionType.TEST, + type: XcType.Button + }, + { + label: 'Save', + placeholder: 'Save', + key: 'save', + actionType: XcActionType.SUBMIT, + type: XcType.Button + } + ], + msgOnInstall: + 'Successfully installed and Twilio is enabled for notification.', + msgOnUninstall: '' } -} +}; -export default config; \ No newline at end of file +export default config; diff --git a/packages/nocodb/src/plugins/twilioWhatsapp/TwilioWhatsapp.ts b/packages/nocodb/src/plugins/twilioWhatsapp/TwilioWhatsapp.ts index f66d0f2fb3..4b01db3aa2 100644 --- a/packages/nocodb/src/plugins/twilioWhatsapp/TwilioWhatsapp.ts +++ b/packages/nocodb/src/plugins/twilioWhatsapp/TwilioWhatsapp.ts @@ -1,6 +1,5 @@ -import {IWebhookNotificationAdapter} from "nc-plugin"; -import twilio from "twilio"; - +import { IWebhookNotificationAdapter } from 'nc-plugin'; +import twilio from 'twilio'; export default class TwilioWhatsapp implements IWebhookNotificationAdapter { private input: any; @@ -17,22 +16,18 @@ export default class TwilioWhatsapp implements IWebhookNotificationAdapter { public async sendMessage(content: string, payload: any): Promise { for (const num of payload?.to?.split(/\s*?,\s*?/)) { try { - await this.client.messages - .create({ - body: content, - from: `whatsapp:${this.input.from}`, - to: `whatsapp:${num}` - }) + await this.client.messages.create({ + body: content, + from: `whatsapp:${this.input.from}`, + to: `whatsapp:${num}` + }); } catch (e) { - console.log(e) + console.log(e); } } } - - } - /** * @copyright Copyright (c) 2021, Xgene Cloud Ltd * diff --git a/packages/nocodb/src/plugins/twilioWhatsapp/TwilioWhatsappPlugin.ts b/packages/nocodb/src/plugins/twilioWhatsapp/TwilioWhatsappPlugin.ts index 1c7f8481d0..d549725ca8 100644 --- a/packages/nocodb/src/plugins/twilioWhatsapp/TwilioWhatsappPlugin.ts +++ b/packages/nocodb/src/plugins/twilioWhatsapp/TwilioWhatsappPlugin.ts @@ -1,13 +1,14 @@ -import {IWebhookNotificationAdapter, XcWebhookNotificationPlugin} from "nc-plugin"; - -import TwilioWhatsapp from "./TwilioWhatsapp"; +import { + IWebhookNotificationAdapter, + XcWebhookNotificationPlugin +} from 'nc-plugin'; +import TwilioWhatsapp from './TwilioWhatsapp'; class TwilioWhatsappPlugin extends XcWebhookNotificationPlugin { - private static notificationAdapter: TwilioWhatsapp; - public getAdapter(): IWebhookNotificationAdapter { + public getAdapter(): IWebhookNotificationAdapter { return TwilioWhatsappPlugin.notificationAdapter; } @@ -15,7 +16,6 @@ class TwilioWhatsappPlugin extends XcWebhookNotificationPlugin { TwilioWhatsappPlugin.notificationAdapter = new TwilioWhatsapp(config); await TwilioWhatsappPlugin.notificationAdapter.init(); } - } export default TwilioWhatsappPlugin; diff --git a/packages/nocodb/src/plugins/twilioWhatsapp/index.ts b/packages/nocodb/src/plugins/twilioWhatsapp/index.ts index 31c79728bd..6fe68baff0 100644 --- a/packages/nocodb/src/plugins/twilioWhatsapp/index.ts +++ b/packages/nocodb/src/plugins/twilioWhatsapp/index.ts @@ -1,54 +1,63 @@ -import {XcActionType, XcType} from "nc-common"; -import {XcPluginConfig} from "nc-plugin"; +import { XcActionType, XcType } from 'nc-common'; +import { XcPluginConfig } from 'nc-plugin'; -import TwilioWhatsappPlugin from "./TwilioWhatsappPlugin"; +import TwilioWhatsappPlugin from './TwilioWhatsappPlugin'; const config: XcPluginConfig = { builder: TwilioWhatsappPlugin, title: 'Whatsapp Twilio', version: '0.0.1', logo: 'plugins/whatsapp.png', - description: 'With Twilio, unite communications and strengthen customer relationships across your business – from marketing and sales to customer service and operations.', + description: + 'With Twilio, unite communications and strengthen customer relationships across your business – from marketing and sales to customer service and operations.', price: 'Free', tags: 'Chat', category: 'Twilio', inputs: { title: 'Configure Twilio', - items: [{ - key: 'sid', - label: 'Account SID', - placeholder: 'Account SID', - type: XcType.SingleLineText, - required: true - }, { - key: 'token', - label: 'Auth Token', - placeholder: 'Auth Token', - type: XcType.Password, - required: true - }, { - key: 'from', - label: 'From Phone Number', - placeholder: 'From Phone Number', - type: XcType.SingleLineText, - required: true - },], - actions: [{ - label: 'Test', - placeholder: 'Test', - key: 'test', - actionType: XcActionType.TEST, - type: XcType.Button - }, { - label: 'Save', - placeholder: 'Save', - key: 'save', - actionType: XcActionType.SUBMIT, - type: XcType.Button - },], - msgOnInstall: 'Successfully installed and Whatsapp Twilio is enabled for notification.', - msgOnUninstall: '', + items: [ + { + key: 'sid', + label: 'Account SID', + placeholder: 'Account SID', + type: XcType.SingleLineText, + required: true + }, + { + key: 'token', + label: 'Auth Token', + placeholder: 'Auth Token', + type: XcType.Password, + required: true + }, + { + key: 'from', + label: 'From Phone Number', + placeholder: 'From Phone Number', + type: XcType.SingleLineText, + required: true + } + ], + actions: [ + { + label: 'Test', + placeholder: 'Test', + key: 'test', + actionType: XcActionType.TEST, + type: XcType.Button + }, + { + label: 'Save', + placeholder: 'Save', + key: 'save', + actionType: XcActionType.SUBMIT, + type: XcType.Button + } + ], + msgOnInstall: + 'Successfully installed and Whatsapp Twilio is enabled for notification.', + msgOnUninstall: '' } -} +}; -export default config; \ No newline at end of file +export default config; diff --git a/packages/nocodb/src/plugins/upcloud/UpCloudPlugin.ts b/packages/nocodb/src/plugins/upcloud/UpCloudPlugin.ts index b66b74a0ca..e97a3e6398 100644 --- a/packages/nocodb/src/plugins/upcloud/UpCloudPlugin.ts +++ b/packages/nocodb/src/plugins/upcloud/UpCloudPlugin.ts @@ -1,13 +1,11 @@ -import {IStorageAdapter, XcStoragePlugin} from "nc-plugin"; - -import UpoCloud from "./UpoCloud"; +import { IStorageAdapter, XcStoragePlugin } from 'nc-plugin'; +import UpoCloud from './UpoCloud'; class UpCloudPlugin extends XcStoragePlugin { - private static storageAdapter: UpoCloud; - public getAdapter(): IStorageAdapter { + public getAdapter(): IStorageAdapter { return UpCloudPlugin.storageAdapter; } @@ -15,7 +13,6 @@ class UpCloudPlugin extends XcStoragePlugin { UpCloudPlugin.storageAdapter = new UpoCloud(config); await UpCloudPlugin.storageAdapter.init(); } - } export default UpCloudPlugin; diff --git a/packages/nocodb/src/plugins/upcloud/UpoCloud.ts b/packages/nocodb/src/plugins/upcloud/UpoCloud.ts index 7a8b520ca5..13abc371ed 100644 --- a/packages/nocodb/src/plugins/upcloud/UpoCloud.ts +++ b/packages/nocodb/src/plugins/upcloud/UpoCloud.ts @@ -1,11 +1,10 @@ -import fs from "fs"; -import path from "path"; +import fs from 'fs'; +import path from 'path'; -import AWS from "aws-sdk"; -import {IStorageAdapter, XcFile} from "nc-plugin"; +import AWS from 'aws-sdk'; +import { IStorageAdapter, XcFile } from 'nc-plugin'; export default class UpoCloud implements IStorageAdapter { - private s3Client: AWS.S3; private input: any; @@ -13,16 +12,14 @@ export default class UpoCloud implements IStorageAdapter { this.input = input; } - async fileCreate(key: string, file: XcFile): Promise { - const uploadParams: any = { ACL: 'public-read' }; return new Promise((resolve, reject) => { // Configure the file stream and obtain the upload parameters const fileStream = fs.createReadStream(file.path); - fileStream.on('error', (err) => { + fileStream.on('error', err => { console.log('File Error', err); reject(err); }); @@ -33,7 +30,7 @@ export default class UpoCloud implements IStorageAdapter { // call S3 to retrieve upload file to specified bucket this.s3Client.upload(uploadParams, (err, data) => { if (err) { - console.log("Error", err); + console.log('Error', err); reject(err); } if (data) { @@ -49,7 +46,7 @@ export default class UpoCloud implements IStorageAdapter { public async fileRead(key: string): Promise { return new Promise((resolve, reject) => { - this.s3Client.getObject({Key: key} as any, (err, data) => { + this.s3Client.getObject({ Key: key } as any, (err, data) => { if (err) { return reject(err); } @@ -62,19 +59,17 @@ export default class UpoCloud implements IStorageAdapter { } public async init(): Promise { - const s3Options: any = { - params: {Bucket: this.input.bucket}, + params: { Bucket: this.input.bucket }, region: this.input.region }; - s3Options.accessKeyId = this.input.access_key + s3Options.accessKeyId = this.input.access_key; s3Options.secretAccessKey = this.input.access_secret; s3Options.endpoint = new AWS.Endpoint(this.input.endpoint); this.s3Client = new AWS.S3(s3Options); - } public async test(): Promise { @@ -94,10 +89,8 @@ export default class UpoCloud implements IStorageAdapter { throw e; } } - } - /** * @copyright Copyright (c) 2021, Xgene Cloud Ltd * diff --git a/packages/nocodb/src/plugins/upcloud/index.ts b/packages/nocodb/src/plugins/upcloud/index.ts index 50ada3bc6e..bbfda0b2fa 100644 --- a/packages/nocodb/src/plugins/upcloud/index.ts +++ b/packages/nocodb/src/plugins/upcloud/index.ts @@ -1,25 +1,28 @@ -import {XcActionType, XcType} from "nc-common"; -import {XcPluginConfig} from "nc-plugin"; +import { XcActionType, XcType } from 'nc-common'; +import { XcPluginConfig } from 'nc-plugin'; -import UpCloudPlugin from "./UpCloudPlugin"; +import UpCloudPlugin from './UpCloudPlugin'; const config: XcPluginConfig = { builder: UpCloudPlugin, title: 'UpCloud Object Storage', version: '0.0.1', logo: 'plugins/upcloud.png', - description: 'The perfect home for your data. Thanks to the S3-compatible programmable interface,\n' + + description: + 'The perfect home for your data. Thanks to the S3-compatible programmable interface,\n' + 'you have a host of options for existing tools and code implementations.\n', - tags: 'Storage', inputs: { + tags: 'Storage', + inputs: { title: 'Configure UpCloud Object Storage', - items: [{ - key: 'bucket', - label: 'Bucket Name', - placeholder: 'Bucket Name', - type: XcType.SingleLineText, - required: true - }, - { + items: [ + { + key: 'bucket', + label: 'Bucket Name', + placeholder: 'Bucket Name', + type: XcType.SingleLineText, + required: true + }, + { key: 'endpoint', label: 'Endpoint', placeholder: 'Endpoint', @@ -32,30 +35,36 @@ const config: XcPluginConfig = { placeholder: 'Access Key', type: XcType.SingleLineText, required: true - }, { + }, + { key: 'access_secret', label: 'Access Secret', placeholder: 'Access Secret', type: XcType.Password, required: true - },], - actions: [{ - label: 'Test', - placeholder: 'Test', - key: 'test', - actionType: XcActionType.TEST, - type: XcType.Button - }, { - label: 'Save', - placeholder: 'Save', - key: 'save', - actionType: XcActionType.SUBMIT, - type: XcType.Button - },], - msgOnInstall: 'Successfully installed and attachment will be stored in UpCloud Object Storage', - msgOnUninstall: '', + } + ], + actions: [ + { + label: 'Test', + placeholder: 'Test', + key: 'test', + actionType: XcActionType.TEST, + type: XcType.Button + }, + { + label: 'Save', + placeholder: 'Save', + key: 'save', + actionType: XcActionType.SUBMIT, + type: XcType.Button + } + ], + msgOnInstall: + 'Successfully installed and attachment will be stored in UpCloud Object Storage', + msgOnUninstall: '' }, - category: 'Storage', -} + category: 'Storage' +}; -export default config; \ No newline at end of file +export default config; diff --git a/packages/nocodb/src/plugins/vultr/Vultr.ts b/packages/nocodb/src/plugins/vultr/Vultr.ts index 56b7fd1d00..d86154f005 100644 --- a/packages/nocodb/src/plugins/vultr/Vultr.ts +++ b/packages/nocodb/src/plugins/vultr/Vultr.ts @@ -1,11 +1,10 @@ -import fs from "fs"; -import path from "path"; +import fs from 'fs'; +import path from 'path'; -import AWS from "aws-sdk"; -import {IStorageAdapter, XcFile} from "nc-plugin"; +import AWS from 'aws-sdk'; +import { IStorageAdapter, XcFile } from 'nc-plugin'; export default class Vultr implements IStorageAdapter { - private s3Client: AWS.S3; private input: any; @@ -13,16 +12,14 @@ export default class Vultr implements IStorageAdapter { this.input = input; } - async fileCreate(key: string, file: XcFile): Promise { - const uploadParams: any = { ACL: 'public-read' }; return new Promise((resolve, reject) => { // Configure the file stream and obtain the upload parameters const fileStream = fs.createReadStream(file.path); - fileStream.on('error', (err) => { + fileStream.on('error', err => { console.log('File Error', err); reject(err); }); @@ -33,7 +30,7 @@ export default class Vultr implements IStorageAdapter { // call S3 to retrieve upload file to specified bucket this.s3Client.upload(uploadParams, (err, data) => { if (err) { - console.log("Error", err); + console.log('Error', err); reject(err); } if (data) { @@ -49,7 +46,7 @@ export default class Vultr implements IStorageAdapter { public async fileRead(key: string): Promise { return new Promise((resolve, reject) => { - this.s3Client.getObject({Key: key} as any, (err, data) => { + this.s3Client.getObject({ Key: key } as any, (err, data) => { if (err) { return reject(err); } @@ -62,19 +59,19 @@ export default class Vultr implements IStorageAdapter { } public async init(): Promise { - const s3Options: any = { - params: {Bucket: this.input.bucket}, + params: { Bucket: this.input.bucket }, region: this.input.region }; - s3Options.accessKeyId = this.input.access_key + s3Options.accessKeyId = this.input.access_key; s3Options.secretAccessKey = this.input.access_secret; - s3Options.endpoint = new AWS.Endpoint(`s3.${this.input.region}.cloud.ovh.net`); + s3Options.endpoint = new AWS.Endpoint( + `s3.${this.input.region}.cloud.ovh.net` + ); this.s3Client = new AWS.S3(s3Options); - } public async test(): Promise { @@ -94,10 +91,8 @@ export default class Vultr implements IStorageAdapter { throw e; } } - } - /** * @copyright Copyright (c) 2021, Xgene Cloud Ltd * diff --git a/packages/nocodb/src/plugins/vultr/VultrPlugin.ts b/packages/nocodb/src/plugins/vultr/VultrPlugin.ts index 5ed4073ee5..88f2cb64db 100644 --- a/packages/nocodb/src/plugins/vultr/VultrPlugin.ts +++ b/packages/nocodb/src/plugins/vultr/VultrPlugin.ts @@ -1,13 +1,11 @@ -import {IStorageAdapter, XcStoragePlugin} from "nc-plugin"; - -import Vultr from "./Vultr"; +import { IStorageAdapter, XcStoragePlugin } from 'nc-plugin'; +import Vultr from './Vultr'; class VultrPlugin extends XcStoragePlugin { - private static storageAdapter: Vultr; - public getAdapter(): IStorageAdapter { + public getAdapter(): IStorageAdapter { return VultrPlugin.storageAdapter; } @@ -15,7 +13,6 @@ class VultrPlugin extends XcStoragePlugin { VultrPlugin.storageAdapter = new Vultr(config); await VultrPlugin.storageAdapter.init(); } - } export default VultrPlugin; diff --git a/packages/nocodb/src/plugins/vultr/index.ts b/packages/nocodb/src/plugins/vultr/index.ts index 290cf60383..80ce8048e9 100644 --- a/packages/nocodb/src/plugins/vultr/index.ts +++ b/packages/nocodb/src/plugins/vultr/index.ts @@ -1,23 +1,26 @@ -import {XcActionType, XcType} from "nc-common"; -import {XcPluginConfig} from "nc-plugin"; +import { XcActionType, XcType } from 'nc-common'; +import { XcPluginConfig } from 'nc-plugin'; -import VultrPlugin from "./VultrPlugin"; +import VultrPlugin from './VultrPlugin'; const config: XcPluginConfig = { builder: VultrPlugin, title: 'Vultr Object Storage', version: '0.0.1', logo: 'plugins/vultr.png', - description: 'Using Vultr Object Storage can give flexibility and cloud storage that allows applications greater flexibility and access worldwide.', - tags: 'Storage', inputs: { + description: + 'Using Vultr Object Storage can give flexibility and cloud storage that allows applications greater flexibility and access worldwide.', + tags: 'Storage', + inputs: { title: 'Configure Vultr Object Storage', - items: [{ - key: 'bucket', - label: 'Bucket Name', - placeholder: 'Bucket Name', - type: XcType.SingleLineText, - required: true - }, + items: [ + { + key: 'bucket', + label: 'Bucket Name', + placeholder: 'Bucket Name', + type: XcType.SingleLineText, + required: true + }, // { // key: 'region', // label: 'Region', @@ -31,30 +34,36 @@ const config: XcPluginConfig = { placeholder: 'Access Key', type: XcType.SingleLineText, required: true - }, { + }, + { key: 'access_secret', label: 'Access Secret', placeholder: 'Access Secret', type: XcType.Password, required: true - },], - actions: [{ - label: 'Test', - placeholder: 'Test', - key: 'test', - actionType: XcActionType.TEST, - type: XcType.Button - }, { - label: 'Save', - placeholder: 'Save', - key: 'save', - actionType: XcActionType.SUBMIT, - type: XcType.Button - },], - msgOnInstall: 'Successfully installed and attachment will be stored in Vultr Object Storage', - msgOnUninstall: '', + } + ], + actions: [ + { + label: 'Test', + placeholder: 'Test', + key: 'test', + actionType: XcActionType.TEST, + type: XcType.Button + }, + { + label: 'Save', + placeholder: 'Save', + key: 'save', + actionType: XcActionType.SUBMIT, + type: XcType.Button + } + ], + msgOnInstall: + 'Successfully installed and attachment will be stored in Vultr Object Storage', + msgOnUninstall: '' }, - category: 'Storage', -} + category: 'Storage' +}; -export default config; \ No newline at end of file +export default config; diff --git a/packages/nocodb/src/types/global.d.ts b/packages/nocodb/src/types/global.d.ts index 5ecb74d4e3..e777e2ce25 100644 --- a/packages/nocodb/src/types/global.d.ts +++ b/packages/nocodb/src/types/global.d.ts @@ -1,12 +1,13 @@ // @ts-ignore declare global { -// @ts-ignore + // @ts-ignore declare module 'express' { export interface Response { xcJson(body: any): any; } } -}/** +} +/** * @copyright Copyright (c) 2021, Xgene Cloud Ltd * * @author Naveen MR