Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions packages/cubejs-api-gateway/src/sql-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -361,8 +361,8 @@ export class SQLServer {
}

protected createDefaultCheckSqlAuthFn(options: SQLServerOptions): CheckSQLAuthFn {
let allowedUser: string | null = options.sqlUser || getEnv('sqlUser');
let allowedPassword: string | null = options.sqlPassword || getEnv('sqlPassword');
let allowedUser: string | null = options.sqlUser || getEnv('sqlUser') || null;
let allowedPassword: string | null = options.sqlPassword || getEnv('sqlPassword') || null;

if (!getEnv('devMode')) {
if (!allowedUser) {
Expand Down
11 changes: 1 addition & 10 deletions packages/cubejs-api-gateway/src/types/strings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,15 +100,7 @@ type QueryOrderType =
'asc' |
'desc';

/**
* ApiScopes data type.
*/
type ApiScopes =
'graphql' |
'meta' |
'data' |
'sql' |
'jobs';
export type { ApiScopes } from '@cubejs-backend/shared';

export {
RequestType,
Expand All @@ -121,5 +113,4 @@ export {
FilterOperator,
QueryTimeDimensionGranularity,
QueryOrderType,
ApiScopes,
};
2 changes: 1 addition & 1 deletion packages/cubejs-athena-driver/src/AthenaDriver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ export class AthenaDriver extends BaseDriver implements DriverInterface {

private athena: Athena;

private schema: string;
private schema: string | undefined;

/**
* Class constructor.
Expand Down
93 changes: 79 additions & 14 deletions packages/cubejs-backend-shared/src/env.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
/* eslint-disable no-restricted-syntax */
/* eslint-disable no-restricted-syntax,no-use-before-define */
import { get } from 'env-var';
import { displayCLIWarning } from './cli';
import { isNativeSupported } from './platform';
import type { LogLevel } from './logger';
import type { ApiScopes, ExportBucketType } from './shared-types';

export class InvalidConfiguration extends Error {
public constructor(key: string, value: any, description: string) {
Expand Down Expand Up @@ -159,11 +161,28 @@ function asBoolOrTime(input: string, envName: string): number | boolean {
);
}

const variables: Record<string, (...args: any) => any> = {
const variables = {
devMode: () => get('CUBEJS_DEV_MODE')
.default('false')
.asBoolStrict(),
logLevel: () => get('CUBEJS_LOG_LEVEL').asString(),
logLevel: (): LogLevel | undefined => {
const value = get('CUBEJS_LOG_LEVEL').asString();
if (value) {
switch (value.toLowerCase()) {
case 'trace':
case 'info':
case 'warn':
case 'error':
break;
// not used, but let's allow
case 'debug':
break;
default:
throw new InvalidConfiguration('CUBEJS_LOG_LEVEL', value, 'Must be one of: trace, debug, info, warn, error');
}
}
return value as LogLevel | undefined;
},
port: () => asPortOrSocket(process.env.PORT || '4000', 'PORT'),
tls: () => get('CUBEJS_ENABLE_TLS')
.default('false')
Expand Down Expand Up @@ -532,6 +551,37 @@ const variables: Record<string, (...args: any) => any> = {
process.env[keyByDataSource('CUBEJS_DB_PASS', dataSource)]
),

/**
* Small helper to simplify getting basicAuth across drivers
*/
dbBasicAuth: ({
dataSource,
}: {
dataSource: string,
}): { user: string; password?: string } | undefined => {
const user = getEnvFn('dbUser')({
dataSource,
});
const password = getEnvFn('dbPass')({
dataSource,
});
if (password && !user) {
throw new Error(
`${keyByDataSource('CUBEJS_DB_USER', dataSource)} must be set when ${keyByDataSource('CUBEJS_DB_PASS', dataSource)} is provided`
);
}

if (user && password) {
return { user, password };
}

if (user) {
return { user };
}

return undefined;
},

/**
* Database name.
*/
Expand Down Expand Up @@ -707,8 +757,8 @@ const variables: Record<string, (...args: any) => any> = {
dbQueryTimeout: ({
dataSource,
}: {
dataSource?: string,
} = {}) => {
dataSource: string,
}) => {
const key = keyByDataSource('CUBEJS_DB_QUERY_TIMEOUT', dataSource);
const value = process.env[key] || '10m';
return convertTimeStrToSeconds(value, key);
Expand Down Expand Up @@ -847,27 +897,28 @@ const variables: Record<string, (...args: any) => any> = {
/**
* Export bucket storage type.
*/
dbExportBucketType: ({
dbExportBucketType: <T extends ExportBucketType>({
supported,
dataSource,
}: {
supported: ('s3' | 'gcp' | 'azure')[],
supported: readonly T[],
dataSource: string,
}) => {
}): T | undefined => {
const val = process.env[
keyByDataSource('CUBEJS_DB_EXPORT_BUCKET_TYPE', dataSource)
];
] as T | undefined;
if (
val &&
supported &&
supported.indexOf(<'s3' | 'gcp' | 'azure'>val) === -1
supported.indexOf(val) === -1
) {
throw new TypeError(
`The ${
keyByDataSource('CUBEJS_DB_EXPORT_BUCKET_TYPE', dataSource)
} must be one of the [${supported.join(', ')}].`
);
}

return val;
},

Expand Down Expand Up @@ -2186,7 +2237,7 @@ const variables: Record<string, (...args: any) => any> = {
cacheAndQueueDriver: () => get('CUBEJS_CACHE_AND_QUEUE_DRIVER')
.asString(),
defaultApiScope: () => get('CUBEJS_DEFAULT_API_SCOPES')
.asArray(','),
.asArray(',') as ApiScopes[] | undefined,
jwkUrl: () => get('CUBEJS_JWK_URL')
.asString(),
jwtKey: () => get('CUBEJS_JWT_KEY')
Expand Down Expand Up @@ -2329,13 +2380,27 @@ const variables: Record<string, (...args: any) => any> = {
.asString(),
accessPolicyMaskNumber: () => get('CUBEJS_ACCESS_POLICY_MASK_NUMBER')
.asString(),
};
} satisfies Record<string, (...args: any[]) => unknown>;

type Vars = typeof variables;

export function getEnv<T extends keyof Vars>(key: T, opts?: Parameters<Vars[T]>): ReturnType<Vars[T]> {
export function getEnvFn<T extends keyof Vars>(key: T): Vars[T] {
if (key in variables) {
return variables[key] as Vars[T];
}

throw new Error(
`Unsupported env variable: "${key}"`,
);
}

/**
* @deprecated Use getEnvFn instead. TypeScript cannot infer return types correctly
* for generic env functions through this wrapper, as ReturnType<> erases generics.
*/
export function getEnv<T extends keyof Vars>(key: T, ...args: Parameters<Vars[T]>): ReturnType<Vars[T]> {
if (key in variables) {
return variables[key](opts);
return (variables[key] as any)(...args);
}

throw new Error(
Expand Down
1 change: 1 addition & 0 deletions packages/cubejs-backend-shared/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export {
getEnv,
getEnvFn,
assertDataSource,
keyByDataSource,
isDockerImage,
Expand Down
13 changes: 13 additions & 0 deletions packages/cubejs-backend-shared/src/shared-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,16 @@ no-cache — AKA “forceRefresh”
Returns fresh data from the database, even if it takes minutes and many “Continue wait” intervals
*/
export type CacheMode = 'stale-if-slow' | 'stale-while-revalidate' | 'must-revalidate' | 'no-cache';

export type ApiScopes =
'graphql' |
'meta' |
'data' |
'sql' |
'jobs';

export type ExportBucketType =
's3' |
'gcp' |
'gcs' |
'azure';
36 changes: 18 additions & 18 deletions packages/cubejs-backend-shared/test/db_env_multi.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -702,20 +702,20 @@ describe('Multiple datasources', () => {
process.env.CUBEJS_DB_EXPORT_BUCKET_TYPE = 'default1';
process.env.CUBEJS_DS_POSTGRES_DB_EXPORT_BUCKET_TYPE = 'postgres1';
process.env.CUBEJS_DS_WRONG_DB_EXPORT_BUCKET_TYPE = 'wrong1';
expect(getEnv('dbExportBucketType', { dataSource: 'default' })).toEqual('default1');
expect(getEnv('dbExportBucketType', { dataSource: 'postgres' })).toEqual('postgres1');
expect(() => getEnv('dbExportBucketType', { dataSource: 'wrong' })).toThrow(
expect(getEnv('dbExportBucketType', { dataSource: 'default', supported: ['default1'] as any })).toEqual('default1');
expect(getEnv('dbExportBucketType', { dataSource: 'postgres', supported: ['postgres1'] as any })).toEqual('postgres1');
expect(() => getEnv('dbExportBucketType', { dataSource: 'wrong', supported: [] })).toThrow(
'The wrong data source is missing in the declared CUBEJS_DATASOURCES.'
);
expect(getEnv('dbExportBucketType', {
dataSource: 'default',
supported: ['default1'],
supported: ['default1'] as any,
})).toEqual('default1');
expect(getEnv('dbExportBucketType', {
dataSource: 'postgres',
supported: ['postgres1'],
supported: ['postgres1'] as any,
})).toEqual('postgres1');
expect(() => getEnv('dbExportBucketType', { dataSource: 'wrong' })).toThrow(
expect(() => getEnv('dbExportBucketType', { dataSource: 'wrong', supported: [] })).toThrow(
'The wrong data source is missing in the declared CUBEJS_DATASOURCES.'
);
expect(() => getEnv('dbExportBucketType', {
Expand All @@ -726,27 +726,27 @@ describe('Multiple datasources', () => {
dataSource: 'postgres',
supported: [],
})).toThrow('The CUBEJS_DS_POSTGRES_DB_EXPORT_BUCKET_TYPE must be one of the [].');
expect(() => getEnv('dbExportBucketType', { dataSource: 'wrong' })).toThrow(
expect(() => getEnv('dbExportBucketType', { dataSource: 'wrong', supported: [] })).toThrow(
'The wrong data source is missing in the declared CUBEJS_DATASOURCES.'
);

process.env.CUBEJS_DB_EXPORT_BUCKET_TYPE = 'default2';
process.env.CUBEJS_DS_POSTGRES_DB_EXPORT_BUCKET_TYPE = 'postgres2';
process.env.CUBEJS_DS_WRONG_DB_EXPORT_BUCKET_TYPE = 'wrong2';
expect(getEnv('dbExportBucketType', { dataSource: 'default' })).toEqual('default2');
expect(getEnv('dbExportBucketType', { dataSource: 'postgres' })).toEqual('postgres2');
expect(() => getEnv('dbExportBucketType', { dataSource: 'wrong' })).toThrow(
expect(getEnv('dbExportBucketType', { dataSource: 'default', supported: ['default2'] as any })).toEqual('default2');
expect(getEnv('dbExportBucketType', { dataSource: 'postgres', supported: ['postgres2'] as any })).toEqual('postgres2');
expect(() => getEnv('dbExportBucketType', { dataSource: 'wrong', supported: [] })).toThrow(
'The wrong data source is missing in the declared CUBEJS_DATASOURCES.'
);
expect(getEnv('dbExportBucketType', {
dataSource: 'default',
supported: ['default2'],
supported: ['default2'] as any,
})).toEqual('default2');
expect(getEnv('dbExportBucketType', {
dataSource: 'postgres',
supported: ['postgres2'],
supported: ['postgres2'] as any,
})).toEqual('postgres2');
expect(() => getEnv('dbExportBucketType', { dataSource: 'wrong' })).toThrow(
expect(() => getEnv('dbExportBucketType', { dataSource: 'wrong', supported: [] })).toThrow(
'The wrong data source is missing in the declared CUBEJS_DATASOURCES.'
);
expect(() => getEnv('dbExportBucketType', {
Expand All @@ -757,16 +757,16 @@ describe('Multiple datasources', () => {
dataSource: 'postgres',
supported: [],
})).toThrow('The CUBEJS_DS_POSTGRES_DB_EXPORT_BUCKET_TYPE must be one of the [].');
expect(() => getEnv('dbExportBucketType', { dataSource: 'wrong' })).toThrow(
expect(() => getEnv('dbExportBucketType', { dataSource: 'wrong', supported: [] })).toThrow(
'The wrong data source is missing in the declared CUBEJS_DATASOURCES.'
);

delete process.env.CUBEJS_DB_EXPORT_BUCKET_TYPE;
delete process.env.CUBEJS_DS_POSTGRES_DB_EXPORT_BUCKET_TYPE;
delete process.env.CUBEJS_DS_WRONG_DB_EXPORT_BUCKET_TYPE;
expect(getEnv('dbExportBucketType', { dataSource: 'default' })).toBeUndefined();
expect(getEnv('dbExportBucketType', { dataSource: 'postgres' })).toBeUndefined();
expect(() => getEnv('dbExportBucketType', { dataSource: 'wrong' })).toThrow(
expect(getEnv('dbExportBucketType', { dataSource: 'default', supported: [] })).toBeUndefined();
expect(getEnv('dbExportBucketType', { dataSource: 'postgres', supported: [] })).toBeUndefined();
expect(() => getEnv('dbExportBucketType', { dataSource: 'wrong', supported: [] })).toThrow(
'The wrong data source is missing in the declared CUBEJS_DATASOURCES.'
);
expect(getEnv('dbExportBucketType', {
Expand All @@ -777,7 +777,7 @@ describe('Multiple datasources', () => {
dataSource: 'postgres',
supported: [],
})).toBeUndefined();
expect(() => getEnv('dbExportBucketType', { dataSource: 'wrong' })).toThrow(
expect(() => getEnv('dbExportBucketType', { dataSource: 'wrong', supported: [] })).toThrow(
'The wrong data source is missing in the declared CUBEJS_DATASOURCES.'
);
});
Expand Down
30 changes: 15 additions & 15 deletions packages/cubejs-backend-shared/test/db_env_single.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -441,20 +441,20 @@ describe('Single datasources', () => {

test('getEnv("dbExportBucketType")', () => {
process.env.CUBEJS_DB_EXPORT_BUCKET_TYPE = 'default1';
expect(getEnv('dbExportBucketType', { dataSource: 'default' })).toEqual('default1');
expect(getEnv('dbExportBucketType', { dataSource: 'postgres' })).toEqual('default1');
expect(getEnv('dbExportBucketType', { dataSource: 'wrong' })).toEqual('default1');
expect(getEnv('dbExportBucketType', { dataSource: 'default', supported: ['default1'] as any })).toEqual('default1');
expect(getEnv('dbExportBucketType', { dataSource: 'postgres', supported: ['default1'] as any })).toEqual('default1');
expect(getEnv('dbExportBucketType', { dataSource: 'wrong', supported: ['default1'] as any })).toEqual('default1');
expect(getEnv('dbExportBucketType', {
dataSource: 'default',
supported: ['default1'],
supported: ['default1'] as any,
})).toEqual('default1');
expect(getEnv('dbExportBucketType', {
dataSource: 'postgres',
supported: ['default1'],
supported: ['default1'] as any,
})).toEqual('default1');
expect(getEnv('dbExportBucketType', {
dataSource: 'wrong',
supported: ['default1'],
supported: ['default1'] as any,
})).toEqual('default1');
expect(() => getEnv('dbExportBucketType', {
dataSource: 'default',
Expand All @@ -470,20 +470,20 @@ describe('Single datasources', () => {
})).toThrow('The CUBEJS_DB_EXPORT_BUCKET_TYPE must be one of the [].');

process.env.CUBEJS_DB_EXPORT_BUCKET_TYPE = 'default2';
expect(getEnv('dbExportBucketType', { dataSource: 'default' })).toEqual('default2');
expect(getEnv('dbExportBucketType', { dataSource: 'postgres' })).toEqual('default2');
expect(getEnv('dbExportBucketType', { dataSource: 'wrong' })).toEqual('default2');
expect(getEnv('dbExportBucketType', { dataSource: 'default', supported: ['default2'] as any })).toEqual('default2');
expect(getEnv('dbExportBucketType', { dataSource: 'postgres', supported: ['default2'] as any })).toEqual('default2');
expect(getEnv('dbExportBucketType', { dataSource: 'wrong', supported: ['default2'] as any })).toEqual('default2');
expect(getEnv('dbExportBucketType', {
dataSource: 'default',
supported: ['default2'],
supported: ['default2'] as any,
})).toEqual('default2');
expect(getEnv('dbExportBucketType', {
dataSource: 'postgres',
supported: ['default2'],
supported: ['default2'] as any,
})).toEqual('default2');
expect(getEnv('dbExportBucketType', {
dataSource: 'wrong',
supported: ['default2'],
supported: ['default2'] as any,
})).toEqual('default2');
expect(() => getEnv('dbExportBucketType', {
dataSource: 'default',
Expand All @@ -499,9 +499,9 @@ describe('Single datasources', () => {
})).toThrow('The CUBEJS_DB_EXPORT_BUCKET_TYPE must be one of the [].');

delete process.env.CUBEJS_DB_EXPORT_BUCKET_TYPE;
expect(getEnv('dbExportBucketType', { dataSource: 'default' })).toBeUndefined();
expect(getEnv('dbExportBucketType', { dataSource: 'postgres' })).toBeUndefined();
expect(getEnv('dbExportBucketType', { dataSource: 'wrong' })).toBeUndefined();
expect(getEnv('dbExportBucketType', { dataSource: 'default', supported: [] })).toBeUndefined();
expect(getEnv('dbExportBucketType', { dataSource: 'postgres', supported: [] })).toBeUndefined();
expect(getEnv('dbExportBucketType', { dataSource: 'wrong', supported: [] })).toBeUndefined();
expect(getEnv('dbExportBucketType', {
dataSource: 'default',
supported: [],
Expand Down
Loading
Loading