Skip to content

Commit 5580018

Browse files
authored
feat: Add server-core options validate (#1089)
1 parent 4f53ac3 commit 5580018

File tree

6 files changed

+210
-23
lines changed

6 files changed

+210
-23
lines changed

CONTRIBUTING.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ The rest will be done by `BaseDriver` class.
4747
6. If db requires connection pooling prefer use `generic-pool` implementation with settings similar to other db packages.
4848
7. Make sure your driver has `release()` method in case DB expects graceful shutdowns for connections.
4949
8. Please use yarn to add any dependencies and run `$ yarn` within the package before committing to ensure right `yarn.lock` is in place.
50-
9. Add this driver dependency to [cubejs-server-core/core/index.js](https://github.com/statsbotco/cube.js/blob/master/packages/cubejs-server-core/core/index.js#L8).
50+
9. Add this driver dependency to [cubejs-server-core/core/DriverDependencies.js](https://github.com/cube-js/cube.js/blob/master/packages/cubejs-server-core/core/DriverDependencies.js#L1).
5151

5252
### Implementing JDBC Driver
5353

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
module.exports = {
2+
postgres: '@cubejs-backend/postgres-driver',
3+
mysql: '@cubejs-backend/mysql-driver',
4+
mssql: '@cubejs-backend/mssql-driver',
5+
athena: '@cubejs-backend/athena-driver',
6+
jdbc: '@cubejs-backend/jdbc-driver',
7+
mongobi: '@cubejs-backend/mongobi-driver',
8+
bigquery: '@cubejs-backend/bigquery-driver',
9+
redshift: '@cubejs-backend/postgres-driver',
10+
clickhouse: '@cubejs-backend/clickhouse-driver',
11+
hive: '@cubejs-backend/hive-driver',
12+
snowflake: '@cubejs-backend/snowflake-driver',
13+
prestodb: '@cubejs-backend/prestodb-driver',
14+
oracle: '@cubejs-backend/oracle-driver',
15+
sqlite: '@cubejs-backend/sqlite-driver',
16+
awselasticsearch: '@cubejs-backend/elasticsearch-driver',
17+
elasticsearch: '@cubejs-backend/elasticsearch-driver',
18+
dremio: '@cubejs-backend/dremio-driver',
19+
};

packages/cubejs-server-core/core/index.js

Lines changed: 3 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -14,26 +14,8 @@ const DevServer = require('./DevServer');
1414
const track = require('./track');
1515
const agentCollect = require('./agentCollect');
1616
const { version } = require('../package.json');
17-
18-
const DriverDependencies = {
19-
postgres: '@cubejs-backend/postgres-driver',
20-
mysql: '@cubejs-backend/mysql-driver',
21-
mssql: '@cubejs-backend/mssql-driver',
22-
athena: '@cubejs-backend/athena-driver',
23-
jdbc: '@cubejs-backend/jdbc-driver',
24-
mongobi: '@cubejs-backend/mongobi-driver',
25-
bigquery: '@cubejs-backend/bigquery-driver',
26-
redshift: '@cubejs-backend/postgres-driver',
27-
clickhouse: '@cubejs-backend/clickhouse-driver',
28-
hive: '@cubejs-backend/hive-driver',
29-
snowflake: '@cubejs-backend/snowflake-driver',
30-
prestodb: '@cubejs-backend/prestodb-driver',
31-
oracle: '@cubejs-backend/oracle-driver',
32-
sqlite: '@cubejs-backend/sqlite-driver',
33-
awselasticsearch: '@cubejs-backend/elasticsearch-driver',
34-
elasticsearch: '@cubejs-backend/elasticsearch-driver',
35-
dremio: '@cubejs-backend/dremio-driver',
36-
};
17+
const DriverDependencies = require('./DriverDependencies');
18+
const optionsValidate = require('./optionsValidate');
3719

3820
const checkEnvForPlaceholders = () => {
3921
const placeholderSubstr = '<YOUR_DB_';
@@ -170,6 +152,7 @@ const prodLogger = (level) => (msg, params) => {
170152

171153
class CubejsServerCore {
172154
constructor(options) {
155+
optionsValidate(options);
173156
options = options || {};
174157
options = {
175158
driverFactory: () => typeof options.dbType === 'string' && CubejsServerCore.createDriver(options.dbType),
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
/* eslint-disable no-new */
2+
/* globals describe,test,expect */
3+
4+
const CubejsServerCore = require('./index');
5+
6+
process.env.CUBEJS_API_SECRET = 'api-secret';
7+
8+
describe('index.test', () => {
9+
test('Should create instance of CubejsServerCore, dbType as string', () => {
10+
expect(new CubejsServerCore({
11+
dbType: 'mysql'
12+
})).toBeInstanceOf(CubejsServerCore);
13+
});
14+
15+
test('Should create instance of CubejsServerCore, dbType as func', () => {
16+
const options = { dbType: () => {} };
17+
18+
expect(new CubejsServerCore(options))
19+
.toBeInstanceOf(CubejsServerCore);
20+
});
21+
22+
test('Should throw error, unknown dbType', () => {
23+
const options = { dbType: 'unknown-db' };
24+
25+
expect(() => { new CubejsServerCore(options); })
26+
.toThrowError(/"dbType" must be one of/);
27+
});
28+
29+
test('Should throw error, unknown options property', () => {
30+
const options = { dbType: 'mysql', unknown: 'some-value' };
31+
32+
expect(() => { new CubejsServerCore(options); })
33+
.toThrowError(/"unknown" is not allowed/);
34+
});
35+
36+
test('Should throw error, invalid options', () => {
37+
const options = {
38+
dbType: 'mysql',
39+
externalDbType: 'mysql',
40+
schemaPath: '/test/path/test/',
41+
basePath: '/basePath',
42+
webSocketsBasePath: '/webSocketsBasePath',
43+
devServer: true,
44+
compilerCacheSize: -10,
45+
};
46+
47+
expect(() => { new CubejsServerCore(options); })
48+
.toThrowError(/"compilerCacheSize" must be larger than or equal to 0/);
49+
});
50+
51+
test('Should create instance of CubejsServerCore, pass all options', () => {
52+
const queueOptions = {
53+
concurrency: 3,
54+
continueWaitTimeout: 5,
55+
executionTimeout: 600,
56+
orphanedTimeout: 120,
57+
heartBeatInterval: 500
58+
};
59+
60+
const options = {
61+
dbType: 'mysql',
62+
externalDbType: 'mysql',
63+
schemaPath: '/test/path/test/',
64+
basePath: '/basePath',
65+
webSocketsBasePath: '/webSocketsBasePath',
66+
devServer: false,
67+
logger: () => {},
68+
driverFactory: () => {},
69+
externalDriverFactory: () => {},
70+
contextToAppId: () => {},
71+
contextToDataSourceId: () => {},
72+
repositoryFactory: () => {},
73+
checkAuth: () => {},
74+
checkAuthMiddleware: () => {},
75+
queryTransformer: () => {},
76+
preAggregationsSchema: () => {},
77+
schemaVersion: () => {},
78+
extendContext: () => {},
79+
scheduledRefreshTimer: true,
80+
compilerCacheSize: 1000,
81+
maxCompilerCacheKeepAlive: 10,
82+
updateCompilerCacheKeepAlive: true,
83+
telemetry: false,
84+
allowUngroupedWithoutPrimaryKey: true,
85+
orchestratorOptions: {
86+
redisPrefix: 'some-prefix',
87+
queryCacheOptions: {
88+
refreshKeyRenewalThreshold: 1000,
89+
backgroundRenew: true,
90+
queueOptions
91+
},
92+
preAggregationsOptions: {
93+
queueOptions
94+
}
95+
}
96+
};
97+
98+
expect(new CubejsServerCore(options))
99+
.toBeInstanceOf(CubejsServerCore);
100+
});
101+
102+
test('Should create instance of CubejsServerCore, dbType from process.env.CUBEJS_DB_TYPE', () => {
103+
process.env.CUBEJS_DB_TYPE = 'mysql';
104+
105+
expect(new CubejsServerCore({}))
106+
.toBeInstanceOf(CubejsServerCore);
107+
});
108+
109+
test('Should throw error, dbType is required', () => {
110+
delete process.env.CUBEJS_DB_TYPE;
111+
112+
expect(() => { new CubejsServerCore({}); })
113+
.toThrowError(/driverFactory, apiSecret, dbType are required options/);
114+
});
115+
});
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
const Joi = require('@hapi/joi');
2+
const DriverDependencies = require('./DriverDependencies');
3+
4+
5+
const schemaQueueOptions = Joi.object().keys({
6+
concurrency: Joi.number().min(1).integer(),
7+
continueWaitTimeout: Joi.number().min(0).integer(),
8+
executionTimeout: Joi.number().min(0).integer(),
9+
orphanedTimeout: Joi.number().min(0).integer(),
10+
heartBeatInterval: Joi.number().min(0).integer()
11+
});
12+
13+
const dbTypes = Joi.alternatives().try(
14+
Joi.string().valid(...Object.keys(DriverDependencies)),
15+
Joi.func()
16+
);
17+
18+
const schemaOptions = Joi.object().keys({
19+
dbType: dbTypes,
20+
externalDbType: dbTypes,
21+
schemaPath: Joi.string(),
22+
basePath: Joi.string(),
23+
webSocketsBasePath: Joi.string(),
24+
devServer: Joi.boolean(),
25+
logger: Joi.func(),
26+
driverFactory: Joi.func(),
27+
externalDriverFactory: Joi.func(),
28+
contextToAppId: Joi.func(),
29+
contextToDataSourceId: Joi.func(),
30+
repositoryFactory: Joi.func(),
31+
checkAuth: Joi.func(),
32+
checkAuthMiddleware: Joi.func(),
33+
queryTransformer: Joi.func(),
34+
preAggregationsSchema: Joi.alternatives().try(
35+
Joi.string(),
36+
Joi.func()
37+
),
38+
schemaVersion: Joi.func(),
39+
extendContext: Joi.func(),
40+
scheduledRefreshTimer: Joi.alternatives().try(
41+
Joi.boolean(),
42+
Joi.number().min(0).integer()
43+
),
44+
compilerCacheSize: Joi.number().min(0).integer(),
45+
maxCompilerCacheKeepAlive: Joi.number().min(0).integer(),
46+
updateCompilerCacheKeepAlive: Joi.boolean(),
47+
telemetry: Joi.boolean(),
48+
allowUngroupedWithoutPrimaryKey: Joi.boolean(),
49+
orchestratorOptions: Joi.object().keys({
50+
redisPrefix: Joi.string().allow(''),
51+
queryCacheOptions: Joi.object().keys({
52+
refreshKeyRenewalThreshold: Joi.number().min(0).integer(),
53+
backgroundRenew: Joi.boolean(),
54+
queueOptions: schemaQueueOptions
55+
}),
56+
preAggregationsOptions: {
57+
queueOptions: schemaQueueOptions
58+
}
59+
})
60+
});
61+
62+
63+
module.exports = (options) => {
64+
const { error } = Joi.validate(options, schemaOptions, { abortEarly: false });
65+
if (error) throw new Error(`Invalid cube-server-core options: ${error.message || error.toString()}`);
66+
};

packages/cubejs-server-core/package.json

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,10 @@
1919
"playground"
2020
],
2121
"scripts": {
22-
"lint": "eslint core/**/*.js"
22+
"lint": "eslint core/**/*.js",
23+
"test": "npm run test:unit",
24+
"test:unit": "jest ./**/*.test.js"
25+
2326
},
2427
"dependencies": {
2528
"@cubejs-backend/api-gateway": "^0.20.7",
@@ -40,7 +43,8 @@
4043
"semver": "^6.3.0",
4144
"serve-static": "^1.13.2",
4245
"sqlstring": "^2.3.1",
43-
"uuid": "^3.3.3"
46+
"uuid": "^3.3.3",
47+
"@hapi/joi": "^15.1.1"
4448
},
4549
"devDependencies": {
4650
"eslint": "^6.8.0",

0 commit comments

Comments
 (0)