Skip to content

Commit 806db35

Browse files
authored
feat(native): cube.py - support file_repository (#7107)
1 parent 9ecb180 commit 806db35

File tree

9 files changed

+127
-9
lines changed

9 files changed

+127
-9
lines changed

packages/cubejs-backend-native/Cargo.lock

Lines changed: 7 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/cubejs-backend-native/js/index.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,7 @@ export const shutdownInterface = async (instance: SqlInterfaceInstance): Promise
311311
};
312312

313313
export interface PyConfiguration {
314+
repositoryFactory?: (ctx: unknown) => Promise<unknown>,
314315
checkAuth?: (req: unknown, authorization: string) => Promise<void>
315316
queryRewrite?: (query: unknown, ctx: unknown) => Promise<unknown>
316317
contextToApiScopes?: () => Promise<string[]>
@@ -337,6 +338,15 @@ export const pythonLoadConfig = async (content: string, options: { fileName: str
337338
);
338339
}
339340

341+
if (config.repositoryFactory) {
342+
const nativeRepositoryFactory = config.repositoryFactory;
343+
config.repositoryFactory = (ctx: any) => ({
344+
dataSchemaFiles: async () => nativeRepositoryFactory(
345+
ctx
346+
)
347+
});
348+
}
349+
340350
return config;
341351
};
342352

packages/cubejs-backend-native/python/cube/src/conf/__init__.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,27 @@
1+
import os
12
from typing import Union, Callable, Dict
23

34

5+
def file_repository(path):
6+
files = []
7+
8+
for (dirpath, dirnames, filenames) in os.walk(path):
9+
for fileName in filenames:
10+
if fileName.endswith(".js") or fileName.endswith(".yml") or fileName.endswith(".yaml") or fileName.endswith(".jinja") or fileName.endswith(".py"):
11+
path = os.path.join(dirpath, fileName)
12+
13+
f = open(path, 'r')
14+
content = f.read()
15+
f.close()
16+
17+
files.append({
18+
'fileName': fileName,
19+
'content': content
20+
})
21+
22+
return files
23+
24+
425
class RequestContext:
526
url: str
627
method: str
@@ -29,6 +50,7 @@ class Configuration:
2950
extend_context: Callable
3051
scheduled_refresh_contexts: Callable
3152
context_to_api_scopes: Callable
53+
repository_factory: Callable
3254

3355
def __init__(self):
3456
self.schema_path = None
@@ -53,6 +75,7 @@ def __init__(self):
5375
self.extend_context = None
5476
self.scheduled_refresh_contexts = None
5577
self.context_to_api_scopes = None
78+
self.repository_factory = None
5679

5780
def set_schema_path(self, schema_path: str):
5881
self.schema_path = schema_path
@@ -114,5 +137,8 @@ def set_extend_context(self, extend_context: Callable[[RequestContext], Dict]):
114137
def set_scheduled_refresh_contexts(self, scheduled_refresh_contexts: Callable):
115138
self.scheduled_refresh_contexts = scheduled_refresh_contexts
116139

140+
def set_repository_factory(self, repository_factory: Callable):
141+
self.repository_factory = repository_factory
142+
117143

118144
settings = Configuration()

packages/cubejs-backend-native/src/python/cross.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use neon::prelude::*;
44
use neon::result::Throw;
55
use neon::types::JsDate;
66
use pyo3::exceptions::{PyNotImplementedError, PyTypeError};
7-
use pyo3::types::{PyBool, PyDict, PyFloat, PyFunction, PyInt, PyList, PyString};
7+
use pyo3::types::{PyBool, PyDict, PyFloat, PyFunction, PyInt, PyList, PySet, PyString};
88
use pyo3::{Py, PyAny, PyErr, PyObject, Python, ToPyObject};
99
use std::cell::RefCell;
1010
use std::collections::hash_map::{IntoIter, Iter, Keys};
@@ -264,6 +264,15 @@ impl CLRepr {
264264
r.push(Self::from_python_ref(v)?);
265265
}
266266

267+
Self::Array(r)
268+
} else if v.get_type().is_subclass_of::<PySet>()? {
269+
let l = v.downcast::<PySet>()?;
270+
let mut r = Vec::with_capacity(l.len());
271+
272+
for v in l.iter() {
273+
r.push(Self::from_python_ref(v)?);
274+
}
275+
267276
Self::Array(r)
268277
} else {
269278
return Err(PyErr::new::<PyTypeError, _>(format!(

packages/cubejs-backend-native/src/python/cube_config.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ impl CubeConfigPy {
4444
self.function_attr(config_module, "extend_context")?;
4545
self.function_attr(config_module, "scheduled_refresh_contexts")?;
4646
self.function_attr(config_module, "context_to_api_scopes")?;
47+
self.function_attr(config_module, "repository_factory")?;
4748

4849
Ok(())
4950
}

packages/cubejs-backend-native/test/config.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
from cube.conf import settings
1+
from cube.conf import (
2+
settings,
3+
file_repository
4+
)
25

36
settings.schema_path = "models"
47
settings.pg_sql_port = 5555
@@ -16,6 +19,13 @@ async def check_auth(req, authorization):
1619

1720
settings.check_auth = check_auth
1821

22+
async def repository_factory(ctx):
23+
print('[python] repository_factory ctx=', ctx)
24+
25+
return file_repository(ctx['securityContext']['schemaPath'])
26+
27+
settings.repository_factory = repository_factory
28+
1929
async def context_to_api_scopes():
2030
print('[python] context_to_api_scopes')
2131
return ['meta', 'data', 'jobs']
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
cubes:
2+
- name: cube_01
3+
sql_table: 'kek'
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
{%- set payment_methods = [
2+
"bank_transfer",
3+
"credit_card",
4+
"gift_card"
5+
] -%}
6+
7+
cubes:
8+
- name: cube_01_1
9+
sql: >
10+
SELECT
11+
order_id,
12+
{%- for payment_method in payment_methods %}
13+
SUM(CASE WHEN payment_method = '{{payment_method}}' THEN amount END) AS {{payment_method}}_amount,
14+
{%- endfor %}
15+
SUM(amount) AS total_amount
16+
FROM app_data.payments
17+
GROUP BY 1
18+
19+
- name: cube_01_2
20+
sql: >
21+
SELECT
22+
order_id,
23+
{%- for payment_method in payment_methods %}
24+
SUM(CASE WHEN payment_method = '{{payment_method}}' THEN amount END) AS {{payment_method}}_amount
25+
{%- if not loop.last %},{% endif %}
26+
{%- endfor %}
27+
FROM app_data.payments
28+
GROUP BY 1

packages/cubejs-backend-native/test/python.test.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ suite('Python Config', () => {
4242
contextToApiScopes: expect.any(Function),
4343
checkAuth: expect.any(Function),
4444
queryRewrite: expect.any(Function),
45+
repositoryFactory: expect.any(Function),
4546
});
4647

4748
if (!config.checkAuth) {
@@ -62,6 +63,31 @@ suite('Python Config', () => {
6263
expect(await config.contextToApiScopes()).toEqual(['meta', 'data', 'jobs']);
6364
});
6465

66+
test('repository factory', async () => {
67+
if (!config.repositoryFactory) {
68+
throw new Error('repositoryFactory was not defined in config.py');
69+
}
70+
71+
const ctx = {
72+
securityContext: { schemaPath: path.join(process.cwd(), 'test', 'fixtures', 'schema-tenant-1') }
73+
};
74+
75+
const repository: any = await config.repositoryFactory(ctx);
76+
expect(repository).toEqual({
77+
dataSchemaFiles: expect.any(Function)
78+
});
79+
80+
const files = await repository.dataSchemaFiles();
81+
expect(files).toContainEqual({
82+
fileName: 'test.yml',
83+
content: expect.any(String),
84+
});
85+
expect(files).toContainEqual({
86+
fileName: 'test.yml.jinja',
87+
content: expect.any(String),
88+
});
89+
});
90+
6591
test('cross language converting (js -> python -> js)', async () => {
6692
if (!config.queryRewrite) {
6793
throw new Error('queryRewrite was not defined in config.py');
@@ -81,6 +107,11 @@ suite('Python Config', () => {
81107
obj: {
82108
field_str: 'string',
83109
},
110+
obj_with_nested_object: {
111+
sub_object: {
112+
sub_field_str: 'string'
113+
}
114+
},
84115
array_int: [1, 2, 3, 4, 5],
85116
array_obj: [{
86117
field_str_first: 'string',

0 commit comments

Comments
 (0)