dremiojs 1.0.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,59 @@
1
+ # SQL & Jobs API Guide
2
+
3
+ Executing SQL in Dremio is an asynchronous process involving job submission, polling, and result retrieval. `dremiojs` simplifies this with a helper method while still exposing raw controls.
4
+
5
+ ## The Easy Way: `executeQuery`
6
+
7
+ The `executeQuery` method handles the entire lifecycle: submitting the job, polling for completion, and fetching results.
8
+
9
+ ```typescript
10
+ const rows = await client.jobs.executeQuery('SELECT * FROM sys.version');
11
+ console.log(rows);
12
+ // Output: [{ version: '24.2.0', ... }]
13
+ ```
14
+
15
+ ### Context
16
+ You can provide a context for the query (e.g., to simplify table names).
17
+
18
+ ```typescript
19
+ // Queries table inside 'MarketingSpace' without full qualification
20
+ const rows = await client.jobs.executeQuery('SELECT * FROM "Campaigns"', {
21
+ context: ['MarketingSpace']
22
+ });
23
+ ```
24
+
25
+ ## The Query Lifecycle (Manual Control)
26
+
27
+ For long-running queries or advanced UI integration (like progress bars), you may want to manage the lifecycle yourself.
28
+
29
+ ### 1. Submit Job
30
+ ```typescript
31
+ const jobStub = await client.jobs.submitSQL('SELECT count(*) FROM big_table');
32
+ const jobId = jobStub.id;
33
+ console.log('Job submitted:', jobId);
34
+ ```
35
+
36
+ ### 2. Check Status
37
+ Poll the job status until it reaches a terminal state (`COMPLETED`, `FAILED`, `CANCELED`).
38
+
39
+ ```typescript
40
+ const status = await client.jobs.getJobStatus(jobId);
41
+ console.log('Current State:', status.jobState); // e.g., 'RUNNING'
42
+ ```
43
+
44
+ ### 3. Get Results
45
+ Once the job is `COMPLETED`, fetch the results.
46
+
47
+ ```typescript
48
+ // Fetch first 100 rows
49
+ const results = await client.jobs.getJobResults(jobId, 0, 100);
50
+ console.log('Rows:', results.rows);
51
+ console.log('Schema:', results.schema);
52
+ ```
53
+
54
+ ### 4. Cancel Job
55
+ If the user wants to stop the query:
56
+
57
+ ```typescript
58
+ await client.jobs.cancelJob(jobId);
59
+ ```
package/docs/users.md ADDED
@@ -0,0 +1,19 @@
1
+ # Users API Guide
2
+
3
+ The Users API allows you to lookup user information. This is useful for administration and governance workflows.
4
+
5
+ ## Getting a User by ID
6
+
7
+ ```typescript
8
+ const user = await client.users.get('user-guid');
9
+ console.log(user.name, user.email);
10
+ ```
11
+
12
+ ## Getting a User by Name
13
+
14
+ ```typescript
15
+ const user = await client.users.getByName('admin');
16
+ console.log(user.id);
17
+ ```
18
+
19
+ *Note: User management capabilities (Create/Update/Delete) are typically restricted to administrative APIs and may differ significantly between Software and Cloud. The current library focuses on lookup/retrieval.*
package/package.json CHANGED
@@ -1,7 +1,11 @@
1
1
  {
2
2
  "name": "dremiojs",
3
- "version": "1.0.0",
4
- "description": "",
3
+ "version": "1.2.0",
4
+ "description": "A TypeScript client library for Dremio Cloud and Software.",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "git+https://github.com/developer-advocacy-dremio/dremiojs.git"
8
+ },
5
9
  "main": "index.js",
6
10
  "scripts": {
7
11
  "test": "echo \"Error: no test specified\" && exit 1"
@@ -12,6 +16,7 @@
12
16
  "type": "commonjs",
13
17
  "devDependencies": {
14
18
  "@types/jest": "^30.0.0",
19
+ "@types/js-yaml": "^4.0.9",
15
20
  "@types/node": "^25.0.3",
16
21
  "axios": "^1.13.2",
17
22
  "dotenv": "^17.2.3",
@@ -21,5 +26,8 @@
21
26
  "ts-jest": "^29.4.6",
22
27
  "ts-node": "^10.9.2",
23
28
  "typescript": "^5.9.3"
29
+ },
30
+ "dependencies": {
31
+ "js-yaml": "^4.1.1"
24
32
  }
25
33
  }
@@ -0,0 +1,55 @@
1
+ import { BaseClient } from '../client/base';
2
+
3
+ export interface Tag {
4
+ tags: string[];
5
+ version?: string;
6
+ }
7
+
8
+ export interface Wiki {
9
+ text: string;
10
+ version?: string;
11
+ }
12
+
13
+ export class CollaborationResource {
14
+ private client: BaseClient;
15
+
16
+ constructor(client: BaseClient) {
17
+ this.client = client;
18
+ }
19
+
20
+ async getTags(catalogId: string): Promise<Tag> {
21
+ return this.client.request<Tag>({
22
+ method: 'GET',
23
+ url: `/catalog/${encodeURIComponent(catalogId)}/collaboration/tag`
24
+ });
25
+ }
26
+
27
+ async setTags(catalogId: string, tags: string[], version?: string): Promise<void> {
28
+ const data: any = { tags };
29
+ if (version) data.version = version;
30
+
31
+ await this.client.request({
32
+ method: 'POST',
33
+ url: `/catalog/${encodeURIComponent(catalogId)}/collaboration/tag`,
34
+ data
35
+ });
36
+ }
37
+
38
+ async getWiki(catalogId: string): Promise<Wiki> {
39
+ return this.client.request<Wiki>({
40
+ method: 'GET',
41
+ url: `/catalog/${encodeURIComponent(catalogId)}/collaboration/wiki`
42
+ });
43
+ }
44
+
45
+ async setWiki(catalogId: string, text: string, version?: string): Promise<void> {
46
+ const data: any = { text };
47
+ if (version) data.version = version;
48
+
49
+ await this.client.request({
50
+ method: 'POST',
51
+ url: `/catalog/${encodeURIComponent(catalogId)}/collaboration/wiki`,
52
+ data
53
+ });
54
+ }
55
+ }
@@ -0,0 +1,74 @@
1
+ import { BaseClient } from '../client/base';
2
+
3
+ export interface Credential {
4
+ id: string;
5
+ name: string;
6
+ credentialType: 'CLIENT_SECRET' | 'EXTERNAL_JWT';
7
+ clientSecretConfig?: {
8
+ clientId: string;
9
+ clientSecret?: string; // Only returned on creation
10
+ createdAt?: string;
11
+ expiresAt?: string;
12
+ };
13
+ // ... External JWT config properties could be added here
14
+ }
15
+
16
+ export class CredentialsResource {
17
+ private client: BaseClient;
18
+
19
+ constructor(client: BaseClient) {
20
+ this.client = client;
21
+ }
22
+
23
+ /**
24
+ * List credentials for a Service User.
25
+ */
26
+ async list(userId: string): Promise<Credential[]> {
27
+ const response = await this.client.request<Credential[]>({
28
+ method: 'GET',
29
+ url: `/user/${encodeURIComponent(userId)}/oauth/credentials`
30
+ });
31
+ // Response might be wrapped or array, usually array for list endpoints but docs example showed {id...} which is odd for a list.
32
+ // Re-reading docs: GET /user/{id}/oauth/credentials returns A LIST?
33
+ // Actually the doc example showed `curl -X GET .../credentials` returning a SINGLE object?
34
+ // Wait, normally `credentials` implies list. Let's assume list for now or container.
35
+ // Actually, if it returns a single object it might be 'get by id'.
36
+ // Correction: The docs show GET .../credentials returning ONE credential object in the example?
37
+ // That implies you might only have one? Or the doc example is misleading/specific ID.
38
+ // Let's assume it returns an array for safety or we handle both.
39
+ return Array.isArray(response) ? response : [response];
40
+ }
41
+
42
+ /**
43
+ * Create a Client Secret for a Service User.
44
+ * @param userId Service User ID
45
+ * @param name Name for the credential
46
+ * @param lifetimeDays Duration in days (max 180)
47
+ */
48
+ async create(userId: string, name: string, lifetimeDays: number = 180): Promise<Credential> {
49
+ return this.client.request<Credential>({
50
+ method: 'POST',
51
+ url: `/user/${encodeURIComponent(userId)}/oauth/credentials`,
52
+ data: {
53
+ name,
54
+ credentialType: 'CLIENT_SECRET',
55
+ clientSecretConfig: {
56
+ lifetime: {
57
+ quantity: lifetimeDays,
58
+ units: 'DAYS'
59
+ }
60
+ }
61
+ }
62
+ });
63
+ }
64
+
65
+ /**
66
+ * Delete a credential.
67
+ */
68
+ async delete(userId: string, credentialId: string): Promise<void> {
69
+ await this.client.request({
70
+ method: 'DELETE',
71
+ url: `/user/${encodeURIComponent(userId)}/oauth/credentials/${encodeURIComponent(credentialId)}`
72
+ });
73
+ }
74
+ }
@@ -0,0 +1,52 @@
1
+ import { BaseClient } from '../client/base';
2
+
3
+ export interface Grant {
4
+ id: string; // User or Role ID
5
+ granteeType: 'USER' | 'ROLE';
6
+ privileges: string[]; // e.g. ['SELECT', 'ALTER']
7
+ }
8
+
9
+ export interface AccessControlList {
10
+ users?: { id: string, permissions: string[] }[];
11
+ roles?: { id: string, permissions: string[] }[];
12
+ }
13
+
14
+ export class GovernanceResource {
15
+ private client: BaseClient;
16
+
17
+ constructor(client: BaseClient) {
18
+ this.client = client;
19
+ }
20
+
21
+ /**
22
+ * Dremio Cloud: Get Grants for a catalog entity.
23
+ * Dremio Software: Typically uses ACLs embedded in Catalog.
24
+ * This method targets the /grants endpoints primarily used in Cloud/Newer Software.
25
+ */
26
+ async getGrants(catalogId: string): Promise<Grant[]> {
27
+ // Dremio Cloud: /v0/projects/{id}/catalog/{id}/grants
28
+ // Note: ProjectId is handled by client base url interceptor/logic usually.
29
+ const response = await this.client.request<{ grants: Grant[] }>({
30
+ method: 'GET',
31
+ url: `/catalog/${encodeURIComponent(catalogId)}/grants`
32
+ });
33
+ return response.grants || [];
34
+ }
35
+
36
+ /**
37
+ * Dremio Cloud: Update Grants.
38
+ */
39
+ async updateGrants(catalogId: string, grants: Grant[]): Promise<void> {
40
+ await this.client.request({
41
+ method: 'PUT',
42
+ url: `/catalog/${encodeURIComponent(catalogId)}/grants`,
43
+ data: { grants }
44
+ });
45
+ }
46
+
47
+ /**
48
+ * Dremio Software: Set ACL (Software v3 style) on an entity.
49
+ * Technically this usually goes via Catalog Update, but we provide a helper here if beneficial.
50
+ * For now, we'll focus on the 'Grants' endpoint which is cleaner if supported.
51
+ */
52
+ }
@@ -0,0 +1,19 @@
1
+ import { BaseClient } from '../client/base';
2
+
3
+ export class LineageResource {
4
+ private client: BaseClient;
5
+
6
+ constructor(client: BaseClient) {
7
+ this.client = client;
8
+ }
9
+
10
+ async get(catalogId: string): Promise<any> {
11
+ return this.client.request({
12
+ method: 'GET',
13
+ url: `/catalog/${encodeURIComponent(catalogId)}/graph`
14
+ });
15
+ // Note: Graph/Lineage endpoints can vary.
16
+ // Software v3 doc: GET /api/v3/catalog/{id}/graph
17
+ // Cloud doc checks: /v0/projects/{id}/catalog/{id}/graph
18
+ }
19
+ }
@@ -0,0 +1,32 @@
1
+ import { BaseClient } from '../client/base';
2
+
3
+ // Simplified Engine/Queue types
4
+ export interface Engine {
5
+ id: string;
6
+ name: string;
7
+ size?: string;
8
+ status?: string;
9
+ }
10
+
11
+ export class ProvisioningResource {
12
+ private client: BaseClient;
13
+
14
+ constructor(client: BaseClient) {
15
+ this.client = client;
16
+ }
17
+
18
+ /**
19
+ * Dremio Cloud: List Engines
20
+ */
21
+ async listEngines(): Promise<Engine[]> {
22
+ // Cloud: /v0/projects/{id}/engines
23
+ const response = await this.client.request<any>({
24
+ method: 'GET',
25
+ url: '/engines'
26
+ });
27
+ // Normalize response
28
+ return response.data || (Array.isArray(response) ? response : []);
29
+ }
30
+
31
+ // Additional methods for start/stop engines or queue management could be added here
32
+ }
@@ -0,0 +1,64 @@
1
+ import { BaseClient } from '../client/base';
2
+
3
+ export interface Script {
4
+ id: string;
5
+ name: string;
6
+ description?: string;
7
+ content: string; // SQL
8
+ context?: string[];
9
+ createdAt?: string;
10
+ modifiedAt?: string;
11
+ createdBy?: string;
12
+ modifiedBy?: string;
13
+ }
14
+
15
+ export class ScriptsResource {
16
+ private client: BaseClient;
17
+
18
+ constructor(client: BaseClient) {
19
+ this.client = client;
20
+ }
21
+
22
+ async list(options: { maxResults?: number, ownedBy?: string } = {}): Promise<Script[]> {
23
+ const params: any = {};
24
+ if (options.maxResults) params.maxResults = options.maxResults;
25
+ if (options.ownedBy) params.ownedBy = options.ownedBy;
26
+
27
+ const response = await this.client.request<{ data: Script[] }>({
28
+ method: 'GET',
29
+ url: '/scripts',
30
+ params
31
+ });
32
+ return response.data || [];
33
+ }
34
+
35
+ async get(id: string): Promise<Script> {
36
+ return this.client.request<Script>({
37
+ method: 'GET',
38
+ url: `/scripts/${encodeURIComponent(id)}`
39
+ });
40
+ }
41
+
42
+ async create(script: Partial<Script>): Promise<Script> {
43
+ return this.client.request<Script>({
44
+ method: 'POST',
45
+ url: '/scripts',
46
+ data: script
47
+ });
48
+ }
49
+
50
+ async update(id: string, script: Partial<Script>): Promise<Script> {
51
+ return this.client.request<Script>({
52
+ method: 'PUT',
53
+ url: `/scripts/${encodeURIComponent(id)}`,
54
+ data: script
55
+ });
56
+ }
57
+
58
+ async delete(id: string): Promise<void> {
59
+ return this.client.request({
60
+ method: 'DELETE',
61
+ url: `/scripts/${encodeURIComponent(id)}`
62
+ });
63
+ }
64
+ }
@@ -0,0 +1,50 @@
1
+ import { BaseClient } from '../client/base';
2
+
3
+ export class TokenResource {
4
+ private client: BaseClient;
5
+
6
+ constructor(client: BaseClient) {
7
+ this.client = client;
8
+ }
9
+
10
+ // Usually administrative
11
+ // Implement standard PAT creation endpoints if available/documented for target version
12
+
13
+ // Dremio Software: POST /api/v3/user/{uid}/token
14
+ // Dremio Cloud: /v0/projects/{id}/users/{uid}/token ? (Check docs)
15
+ // Often PAT management is done via specialized internal APIs or different paths.
16
+ // We will provide a placeholder that attempts the standard V3 path.
17
+
18
+ // Supports Dremio Cloud (PAT) and Software (PAT) creation
19
+ async createToken(userId: string, label: string, lifetimeDays: number = 30): Promise<{ token: string }> {
20
+ const config = this.client.getConfig();
21
+ let baseUrl = config.baseUrl.replace(/\/$/, '');
22
+
23
+ // Ensure we target the correct API root
24
+ // Cloud: https://api.dremio.cloud/v0/user/{id}/token
25
+ // Software: http://host:9047/api/v3/user/{id}/token
26
+
27
+ // If the client is Cloud, the getConfig() returns the raw URL (without project),
28
+ // effectively handling the path correctly.
29
+
30
+ const fullUrl = `${baseUrl}/user/${encodeURIComponent(userId)}/token`;
31
+
32
+ const millisecondsToExpire = lifetimeDays * 24 * 60 * 60 * 1000;
33
+
34
+ const response = await this.client.request({
35
+ method: 'POST',
36
+ url: fullUrl, // Absolute URL to bypass axios baseURL prefix
37
+ data: {
38
+ label,
39
+ millisecondsToExpire
40
+ }
41
+ });
42
+
43
+ // Cloud API returns the token string directly in some contexts, or JSON?
44
+ // Based on docs example, checking if response is string or object.
45
+ if (typeof response === 'string') {
46
+ return { token: response };
47
+ }
48
+ return response; // Assuming { token: ... } or similar JSON
49
+ }
50
+ }
package/src/api/user.ts CHANGED
@@ -1,12 +1,17 @@
1
1
  import { BaseClient } from '../client/base';
2
2
 
3
+ // Basic types
3
4
  export interface User {
4
5
  id: string;
5
6
  name: string;
6
7
  firstName?: string;
7
8
  lastName?: string;
8
9
  email?: string;
9
- roles?: any[];
10
+ tag?: string;
11
+ identityType?: 'REGULAR_USER' | 'SERVICE_USER';
12
+ active?: boolean;
13
+ roles?: { id: string, name: string, type: string }[];
14
+ oauthClientId?: string; // For Service Users
10
15
  }
11
16
 
12
17
  export class UserResource {
@@ -17,16 +22,47 @@ export class UserResource {
17
22
  }
18
23
 
19
24
  async get(id: string): Promise<User> {
20
- return this.client.request({
25
+ return this.client.request<User>({
21
26
  method: 'GET',
22
27
  url: `/user/${encodeURIComponent(id)}`
23
28
  });
24
29
  }
25
30
 
26
31
  async getByName(name: string): Promise<User> {
27
- return this.client.request({
32
+ return this.client.request<User>({
28
33
  method: 'GET',
29
34
  url: `/user/by-name/${encodeURIComponent(name)}`
30
35
  });
31
36
  }
37
+
38
+ /**
39
+ * Create a new user (Regular or Service).
40
+ * @param user User definition. For Service Users, set identityType to 'SERVICE_USER'.
41
+ */
42
+ async create(user: Partial<User>): Promise<User> {
43
+ return this.client.request<User>({
44
+ method: 'POST',
45
+ url: '/user',
46
+ data: user
47
+ });
48
+ }
49
+
50
+ async update(id: string, user: Partial<User>): Promise<User> {
51
+ return this.client.request<User>({
52
+ method: 'PUT',
53
+ url: `/user/${encodeURIComponent(id)}`,
54
+ data: user
55
+ });
56
+ }
57
+
58
+ async delete(id: string, versionTag?: string): Promise<void> {
59
+ const config: any = {
60
+ method: 'DELETE',
61
+ url: `/user/${encodeURIComponent(id)}`
62
+ };
63
+ if (versionTag) {
64
+ config.params = { version: versionTag };
65
+ }
66
+ await this.client.request(config);
67
+ }
32
68
  }
@@ -5,6 +5,13 @@ import { JobResource } from '../api/jobs';
5
5
  import { ReflectionResource } from '../api/reflection';
6
6
  import { SourceResource } from '../api/source';
7
7
  import { UserResource } from '../api/user';
8
+ import { GovernanceResource } from '../api/governance';
9
+ import { CollaborationResource } from '../api/collaboration';
10
+ import { ScriptsResource } from '../api/scripts';
11
+ import { LineageResource } from '../api/lineage';
12
+ import { ProvisioningResource } from '../api/provisioning';
13
+ import { TokenResource } from '../api/token';
14
+ import { CredentialsResource } from '../api/credentials';
8
15
 
9
16
  export abstract class BaseClient {
10
17
  protected axiosInstance: AxiosInstance;
@@ -14,6 +21,13 @@ export abstract class BaseClient {
14
21
  public reflections: ReflectionResource;
15
22
  public sources: SourceResource;
16
23
  public users: UserResource;
24
+ public governance: GovernanceResource;
25
+ public collaboration: CollaborationResource;
26
+ public scripts: ScriptsResource;
27
+ public lineage: LineageResource;
28
+ public provisioning: ProvisioningResource;
29
+ public tokens: TokenResource;
30
+ public credentials: CredentialsResource;
17
31
 
18
32
  constructor(config: BaseDremioConfig) {
19
33
  this.config = config;
@@ -30,6 +44,13 @@ export abstract class BaseClient {
30
44
  this.reflections = new ReflectionResource(this);
31
45
  this.sources = new SourceResource(this);
32
46
  this.users = new UserResource(this);
47
+ this.governance = new GovernanceResource(this);
48
+ this.collaboration = new CollaborationResource(this);
49
+ this.scripts = new ScriptsResource(this);
50
+ this.lineage = new LineageResource(this);
51
+ this.provisioning = new ProvisioningResource(this);
52
+ this.tokens = new TokenResource(this);
53
+ this.credentials = new CredentialsResource(this);
33
54
 
34
55
  // Add generic interceptors if needed
35
56
  this.axiosInstance.interceptors.response.use(
@@ -41,6 +62,10 @@ export abstract class BaseClient {
41
62
  );
42
63
  }
43
64
 
65
+ public getConfig(): BaseDremioConfig {
66
+ return this.config;
67
+ }
68
+
44
69
  protected abstract getAuthHeaders(): Record<string, string> | Promise<Record<string, string>>;
45
70
 
46
71
  public async request<T = any>(config: AxiosRequestConfig): Promise<T> {
@@ -1,18 +1,25 @@
1
1
  import { BaseClient } from './base';
2
2
  import { DremioCloudConfig } from '../types/config';
3
+ import axios from 'axios';
3
4
 
4
5
  export class DremioCloudClient extends BaseClient {
5
6
  protected config: DremioCloudConfig;
7
+ private sessionToken: string | null = null;
6
8
 
7
9
  constructor(config: DremioCloudConfig) {
8
- if (!config.authToken) {
9
- throw new Error('Auth token is required for Dremio Cloud.');
10
- }
10
+ // Validation: Must have Project ID, and EITHER authToken OR auth.type='pat'
11
11
  if (!config.projectId) {
12
12
  throw new Error('Project ID is required for Dremio Cloud.');
13
13
  }
14
+ const hasLegacyToken = !!config.authToken;
15
+ const hasAuthObj = config.auth && config.auth.type === 'pat';
16
+
17
+ if (!hasLegacyToken && !hasAuthObj) {
18
+ throw new Error('Auth token (PAT) is required for Dremio Cloud.');
19
+ }
14
20
 
15
21
  // Adjust BaseUrl to include project context
22
+ // We do this for the main axios instance, but we keep original config for login
16
23
  const configWithProject = { ...config };
17
24
  if (!configWithProject.baseUrl.includes('/projects/')) {
18
25
  // Remove trailing slash if present
@@ -24,14 +31,53 @@ export class DremioCloudClient extends BaseClient {
24
31
  this.config = config;
25
32
  }
26
33
 
27
- protected getAuthHeaders(): Record<string, string> {
34
+ protected async getAuthHeaders(): Promise<Record<string, string>> {
35
+ if (!this.sessionToken) {
36
+ await this.login();
37
+ }
28
38
  return {
29
- Authorization: `Bearer ${this.config.authToken}`,
39
+ Authorization: `Bearer ${this.sessionToken}`,
30
40
  };
31
41
  }
32
42
 
43
+ private async login(): Promise<void> {
44
+ const pat = this.config.auth?.type === 'pat' ? this.config.auth.token : this.config.authToken;
45
+
46
+ if (!pat) {
47
+ throw new Error('No PAT available for login.');
48
+ }
49
+
50
+ // Try to exchange PAT for Session Token
51
+ try {
52
+ // Original Base URL (e.g. https://api.dremio.cloud/v0)
53
+ const cleanBase = this.config.baseUrl.replace(/\/$/, '');
54
+ const loginUrl = `${cleanBase}/login`;
55
+
56
+ const response = await axios.post(loginUrl, { token: pat }, {
57
+ headers: { 'Content-Type': 'application/json' },
58
+ timeout: this.config.timeout || 10000
59
+ });
60
+
61
+ if (response.data && response.data.token) {
62
+ this.sessionToken = response.data.token;
63
+ } else {
64
+ // Unexpected response structure
65
+ console.warn('Login response did not contain token, falling back to PAT/Legacy mode.');
66
+ this.sessionToken = pat;
67
+ }
68
+ } catch (error) {
69
+ console.warn('PAT Exchange failed, falling back to raw PAT usage. Error:', (error as any).message);
70
+ // Fallback to using PAT directly
71
+ this.sessionToken = pat;
72
+ }
73
+ }
74
+
33
75
  // Cloud specific methods can go here or generic methods can use projectId from config
34
76
  getProjectId(): string {
35
77
  return this.config.projectId;
36
78
  }
79
+
80
+ public getConfig(): DremioCloudConfig {
81
+ return this.config;
82
+ }
37
83
  }