Skip to content

Commit 2f212a1

Browse files
authored
Merge pull request #310 from Canner/feature/cache-impersonate
Feature: extends CacheLayerInfo interface and Profile interface
2 parents cfe52ca + a928615 commit 2f212a1

File tree

12 files changed

+91
-17
lines changed

12 files changed

+91
-17
lines changed

packages/core/src/lib/cache-layer/cacheLayerLoader.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,14 @@ export class CacheLayerLoader implements ICacheLayerLoader {
4242
templateName: string,
4343
cache: CacheLayerInfo
4444
): Promise<void> {
45-
const { cacheTableName, sql, profile, indexes, folderSubpath } = cache;
45+
const {
46+
cacheTableName,
47+
sql,
48+
profile,
49+
indexes,
50+
folderSubpath,
51+
options: cacheOptions,
52+
} = cache;
4653
const type = this.options.type!;
4754
const dataSource = this.dataSourceFactory(profile);
4855

@@ -81,6 +88,7 @@ export class CacheLayerLoader implements ICacheLayerLoader {
8188
directory,
8289
profileName: profile,
8390
type,
91+
options: cacheOptions,
8492
});
8593
} else {
8694
this.logger.debug(

packages/core/src/models/artifact.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,8 @@ export class CacheLayerInfo {
118118
indexes?: Record<string, string>;
119119
// cache folder subpath
120120
folderSubpath?: string;
121+
// options pass to the data source
122+
options?: any;
121123
}
122124

123125
export class APISchema {

packages/core/src/models/extensions/dataSource.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ export interface ExportOptions {
1919
directory: string;
2020
// The profile name to select to export data
2121
profileName: string;
22+
// data source options
23+
options?: any;
2224
// export file format type
2325
type: CacheLayerStoreFormatType | string;
2426
}

packages/core/src/models/profile.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,4 +29,6 @@ export interface Profile<C = Record<string, any>> {
2929
cache?: C;
3030
/** What users have access to this profile */
3131
allow: ProfileAllowConstraints;
32+
/** Properties that can be used when involking the dataSource method */
33+
properties?: Record<string, any>;
3234
}

packages/extension-driver-canner/src/lib/cannerAdapter.ts

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -31,15 +31,23 @@ export class CannerAdapter {
3131
// When querying Canner enterprise, the Canner enterprise will save the query result as parquet files,
3232
// and store them in S3. This method will return the S3 urls of the query result.
3333
// For more Canner API ref: https://docs.cannerdata.com/reference/restful
34-
public async createAsyncQueryResultUrls(sql: string): Promise<string[]> {
34+
public async createAsyncQueryResultUrls(
35+
sql: string,
36+
headers?: Record<string, string>
37+
): Promise<string[]> {
3538
this.logger.debug(`Create async request to Canner.`);
36-
let data = await this.getWorkspaceRequestData('post', '/v2/async-queries', {
37-
data: {
38-
sql,
39-
timeout: 600,
40-
noLimit: true,
39+
let data = await this.getWorkspaceRequestData(
40+
'post',
41+
'/v2/async-queries',
42+
{
43+
data: {
44+
sql,
45+
timeout: 600,
46+
noLimit: true,
47+
},
4148
},
42-
});
49+
headers
50+
);
4351

4452
const { id: requestId } = data;
4553
this.logger.debug(`Wait Async request to finished.`);
@@ -60,14 +68,13 @@ export class CannerAdapter {
6068
private async getWorkspaceRequestData(
6169
method: string,
6270
urlPath: string,
63-
options?: Record<string, any>
71+
options?: Record<string, any>,
72+
headers?: Record<string, string>
6473
) {
6574
await this.prepare();
6675
try {
6776
const response = await axios({
68-
headers: {
69-
Authorization: `Token ${this.PAT}`,
70-
},
77+
headers: { ...headers, Authorization: `Token ${this.PAT}` },
7178
params: {
7279
workspaceSqlName: this.workspaceSqlName,
7380
},
@@ -78,7 +85,9 @@ export class CannerAdapter {
7885
return response.data;
7986
} catch (error: any) {
8087
const message = error.response
81-
? `response status: ${error.response.status}, response data: ${error.response.data}`
88+
? `response status: ${
89+
error.response.status
90+
}, response data: ${JSON.stringify(error.response.data)}`
8291
: `remote server does not response. request ${error.toJSON()}}`;
8392
throw new InternalError(
8493
`Failed to get workspace request "${urlPath}" data, ${message}`

packages/extension-driver-canner/src/lib/cannerDataSource.ts

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ export class CannerDataSource extends DataSource<any, PGOptions> {
2626
private logger = this.getLogger();
2727
protected poolMapping = new Map<
2828
string,
29-
{ pool: Pool; options?: PGOptions }
29+
{ pool: Pool; options?: PGOptions; properties?: Record<string, any> }
3030
>();
3131
protected UserPool = new Map<string, Pool>();
3232

@@ -52,6 +52,7 @@ export class CannerDataSource extends DataSource<any, PGOptions> {
5252
this.poolMapping.set(profile.name, {
5353
pool,
5454
options: profile.connection,
55+
properties: profile.properties,
5556
});
5657
this.logger.debug(`Profile ${profile.name} initialized`);
5758
}
@@ -61,6 +62,7 @@ export class CannerDataSource extends DataSource<any, PGOptions> {
6162
sql,
6263
directory,
6364
profileName,
65+
options: cannerOptions,
6466
}: ExportOptions): Promise<void> {
6567
if (!this.poolMapping.has(profileName)) {
6668
throw new InternalError(`Profile instance ${profileName} not found`);
@@ -69,12 +71,16 @@ export class CannerDataSource extends DataSource<any, PGOptions> {
6971
if (!fs.existsSync(directory)) {
7072
throw new InternalError(`Directory ${directory} not found`);
7173
}
72-
const { options: connection } = this.poolMapping.get(profileName)!;
73-
74+
const { options: connection, properties } =
75+
this.poolMapping.get(profileName)!;
7476
const cannerAdapter = new CannerAdapter(connection);
7577
try {
7678
this.logger.debug('Send the async query to the Canner Enterprise');
77-
const presignedUrls = await cannerAdapter.createAsyncQueryResultUrls(sql);
79+
const header = this.getCannerRequestHeader(properties, cannerOptions);
80+
const presignedUrls = await cannerAdapter.createAsyncQueryResultUrls(
81+
sql,
82+
header
83+
);
7884
this.logger.debug(
7985
'Start fetching the query result parquet files from URLs'
8086
);
@@ -85,6 +91,21 @@ export class CannerDataSource extends DataSource<any, PGOptions> {
8591
throw error;
8692
}
8793
}
94+
private getCannerRequestHeader(
95+
properties?: Record<string, any>,
96+
cannerOptions?: any
97+
) {
98+
const header: Record<string, string> = {};
99+
const userId = cannerOptions?.userId;
100+
const rootUserId = properties?.['rootUserId'];
101+
if (userId && rootUserId) {
102+
header[
103+
'x-trino-session'
104+
] = `root_user_id=${rootUserId}, canner_user_id=${userId}`;
105+
this.logger.debug(`Impersonate used: ${userId}`);
106+
}
107+
return header;
108+
}
88109

89110
private async downloadFiles(urls: string[], directory: string) {
90111
await Promise.all(

packages/extension-driver-canner/test/cannerDataSource.spec.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ it('Data source should export successfully', async () => {
5555
sql: 'select 1',
5656
directory,
5757
profileName: 'profile1',
58+
options: {},
5859
} as ExportOptions)
5960
).resolves.not.toThrow();
6061
expect(fs.readdirSync(directory).length).toBe(1);
@@ -86,6 +87,7 @@ it('Data source should throw error when fail to export data', async () => {
8687
sql: 'select 1',
8788
directory,
8889
profileName: 'profile1',
90+
options: {},
8991
} as ExportOptions)
9092
).rejects.toThrow();
9193
expect(fs.readdirSync(directory).length).toBe(0);
@@ -105,6 +107,7 @@ it('Data source should throw error when given directory is not exist', async ()
105107
sql: 'select 1',
106108
directory: directory,
107109
profileName: 'profile1',
110+
options: {},
108111
} as ExportOptions)
109112
).rejects.toThrow();
110113
}, 100000);
@@ -121,6 +124,7 @@ it('Data source should throw error when given profile name is not exist', async
121124
sql: 'select 1',
122125
directory,
123126
profileName: 'profile not exist',
127+
options: {},
124128
} as ExportOptions)
125129
).rejects.toThrow();
126130
}, 100000);

packages/extension-driver-canner/test/cannerServer.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ export class CannerServer {
2020
database: process.env['CANNER_WORKSPACE_SQL_NAME'],
2121
} as PGOptions,
2222
allow: '*',
23+
properties: {},
2324
};
2425
}
2526
}

packages/extension-store-canner/README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@ export PROFILE_CANNER_DRIVER_PASSWORD=<password>
6363
export PROFILE_CANNER_DRIVER_HOST=<host>
6464
# Canner enterprise driver port, the default is 7432
6565
export PROFILE_CANNER_DRIVER_PORT=<port>
66+
# Canner enterprise root user id
67+
export PROFILE_CANNER_DRIVER_ROOT_USER_ID=<userId>
6668
```
6769

6870
### Connect Canner Enterprise used storage.

packages/extension-store-canner/src/lib/canner/profileReader.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ export class CannerProfileReader extends ProfileReader {
4444

4545
// generate profiles from the indicator files of each workspaces
4646
const { user, password, host, port, max } = this.envConfig.profile;
47+
const { rootUserId } = this.envConfig.properties;
4748
if (!user || !password || !host)
4849
throw new ConfigurationError(
4950
'Canner profile reader needs username, password, host properties.'
@@ -67,6 +68,9 @@ export class CannerProfileReader extends ProfileReader {
6768
max,
6869
},
6970
allow: '*',
71+
properties: {
72+
rootUserId,
73+
},
7074
} as Profile<Record<string, any>>;
7175
this.logger.debug(`created "${profile.name}".`);
7276
return profile;

0 commit comments

Comments
 (0)