dremiojs 1.0.0 → 1.1.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.
package/README.md CHANGED
@@ -57,3 +57,22 @@ async function main() {
57
57
  - **SQL Helper**: Easy `executeQuery` method that handles job submission and polling.
58
58
  - **Catalog Management**: Create, update, delete Spaces, Sources, Folders, and Datasets.
59
59
  - **Reflections**: Manage reflections programmatically.
60
+
61
+ ## Documentation
62
+
63
+ - [Configuration & Environment Variables](docs/configuration.md)
64
+ - [Client Usage](docs/client.md)
65
+ - [Catalog API](docs/catalog.md)
66
+ ## Documentation
67
+
68
+ - [Configuration & Environment Variables](docs/configuration.md)
69
+ - [Client Usage](docs/client.md)
70
+ - [Catalog API](docs/catalog.md)
71
+ - [SQL & Jobs API](docs/sql_and_jobs.md)
72
+ - [Reflections API](docs/reflections.md)
73
+ - [Sources API](docs/sources.md)
74
+ - [Users API](docs/users.md)
75
+ - [Governance & Collaboration](docs/governance_collaboration.md)
76
+ - [Scripts, Lineage & Tokens](docs/scripts_lineage_tokens.md)
77
+ - [Provisioning (Engines)](docs/provisioning.md)
78
+ - [Service Users & Credentials](docs/service_users.md)
@@ -0,0 +1,76 @@
1
+ # Catalog API Guide
2
+
3
+ The Catalog API is the primary way to interact with Dremio's semantic layer (Spaces, Folders, Virtual Datasets) and physical sources.
4
+
5
+ ## Accessing the Catalog
6
+
7
+ ### Listing the Root
8
+ To list the top-level items (Spaces and Sources):
9
+
10
+ ```typescript
11
+ const root = await client.catalog.getByPath([]);
12
+ console.log(root.children);
13
+ ```
14
+
15
+ ### Getting Items by Path
16
+ You can traverse the catalog hierarchy using an array of path segments.
17
+
18
+ ```typescript
19
+ // Get a specific dataset: "MarketingSpace.Campaigns.Q1_Results"
20
+ const dataset = await client.catalog.getByPath(['MarketingSpace', 'Campaigns', 'Q1_Results']);
21
+ console.log(dataset.id, dataset.type);
22
+ ```
23
+
24
+ ### Getting Items by ID
25
+ If you already have the immutable ID of an object (e.g., from a search or list operation):
26
+
27
+ ```typescript
28
+ const item = await client.catalog.get('dremio-dataset-uuid-1234');
29
+ ```
30
+
31
+ ## Creating Items
32
+
33
+ ### Creating a Space
34
+ ```typescript
35
+ await client.catalog.create({
36
+ type: 'SPACE',
37
+ name: 'NewMarketingSpace'
38
+ });
39
+ ```
40
+
41
+ ### Creating a Folder
42
+ ```typescript
43
+ await client.catalog.create({
44
+ type: 'FOLDER',
45
+ path: ['NewMarketingSpace', 'Folder A']
46
+ });
47
+ ```
48
+
49
+ ### Creating a Virtual Dataset (View)
50
+ To create a VDS, you must provide the SQL definition and the context (path).
51
+
52
+ ```typescript
53
+ await client.catalog.create({
54
+ type: 'DATASET',
55
+ path: ['NewMarketingSpace', 'MyView'],
56
+ sql: 'SELECT * FROM "Samples"."samples.dremio.com"."SF_incidents"',
57
+ format: { type: 'VIRTUAL_DATASET' } // Required specific to API v3/Cloud
58
+ });
59
+ ```
60
+
61
+ ## Updating Items
62
+
63
+ ```typescript
64
+ // Rename or Move (check API specifics for move vs update)
65
+ await client.catalog.update('item-id', {
66
+ ...item,
67
+ tag: item.tag, // Optimistic locking tag is often required
68
+ sql: 'SELECT * FROM new_table' // Updating SQL of a view
69
+ });
70
+ ```
71
+
72
+ ## Deleting Items
73
+
74
+ ```typescript
75
+ await client.catalog.delete('item-id');
76
+ ```
package/docs/client.md ADDED
@@ -0,0 +1,55 @@
1
+ # Client Instance Guide
2
+
3
+ The library provides two distinct clients to handle the subtle differences between Dremio Cloud and Dremio Software APIs. Both clients share a common interface for most resources, making it easy to switch between them or support hybrid deployments.
4
+
5
+ ## Dremio Cloud Client
6
+
7
+ The `DremioCloudClient` is optimized for Dremio Cloud's multi-tenant architecture. It automatically handles the `projectId` context required for most API calls.
8
+
9
+ ```typescript
10
+ import { DremioCloudClient } from 'dremiojs';
11
+
12
+ const cloudClient = new DremioCloudClient({
13
+ baseUrl: 'https://api.dremio.cloud/v0',
14
+ authToken: process.env.DREMIO_CLOUD_TOKEN,
15
+ projectId: process.env.DREMIO_CLOUD_PROJECT_ID
16
+ });
17
+
18
+ // The client automatically appends /projects/{id} to relevant paths
19
+ await cloudClient.catalog.getByPath(['space', 'dataset']);
20
+ ```
21
+
22
+ ## Dremio Software Client
23
+
24
+ The `DremioSoftwareClient` supports standard Dremio Software deployments (Docker, K8s, RPM). It includes logic for:
25
+ - API v2 Login (username/password) exchange for tokens.
26
+ - Automatic token management (if using username/pass).
27
+ - Standard v3 API usage.
28
+
29
+ ```typescript
30
+ import { DremioSoftwareClient } from 'dremiojs';
31
+
32
+ const softwareClient = new DremioSoftwareClient({
33
+ baseUrl: 'http://localhost:9047/api/v3',
34
+ authToken: process.env.DREMIO_PAT
35
+ });
36
+
37
+ // Or using Login
38
+ const legacyClient = new DremioSoftwareClient({
39
+ baseUrl: 'http://localhost:9047/api/v3',
40
+ username: 'admin',
41
+ password: 'password123'
42
+ });
43
+
44
+ // The client performs login on the first request if no token is present
45
+ await legacyClient.jobs.executeQuery('SELECT 1');
46
+ ```
47
+
48
+ ## Shared Capabilities
49
+
50
+ Both clients extend `BaseClient` and expose:
51
+ - `catalog`: Manage datasets, spaces, folders.
52
+ - `jobs`: Execute SQL and track jobs.
53
+ - `reflections`: specific Reflection management.
54
+ - `sources`: specific Source management.
55
+ - `users`: User and Role lookups.
@@ -0,0 +1,54 @@
1
+ # Configuration & Environment Variables
2
+
3
+ `dremiojs` is designed to be flexible and secure. It supports configuration via a typed configuration object passed to the client constructor. While it does not automatically read environment variables (to avoid strict dependency on `dotenv` in client-side bundling scenarios), we recommend using standard environment variables in your application.
4
+
5
+ ## Best Practices
6
+
7
+ Store your sensitive credentials in a `.env` file and use a library like `dotenv` to load them.
8
+
9
+ ```bash
10
+ # .env example
11
+ DREMIO_CLOUD_TOKEN=your_pat_here
12
+ DREMIO_CLOUD_PROJECT_ID=your_project_id
13
+ DREMIO_SOFTWARE_URL=http://dremio:9047/api/v3
14
+ DREMIO_SOFTWARE_USER=admin
15
+ DREMIO_SOFTWARE_PASS=password123
16
+ ```
17
+
18
+ ## Configuration Interfaces
19
+
20
+ ### Dremio Cloud Config
21
+
22
+ ```typescript
23
+ interface DremioCloudConfig {
24
+ baseUrl: string; // Usually 'https://api.dremio.cloud/v0'
25
+ authToken: string; // Your Personal Access Token
26
+ projectId: string; // The Project ID provided in the Cloud Console URL
27
+ timeout?: number; // Request timeout in ms (default: 30000)
28
+ }
29
+ ```
30
+
31
+ ### Dremio Software Config
32
+
33
+ ```typescript
34
+ interface DremioSoftwareConfig {
35
+ baseUrl: string; // e.g. 'http://localhost:9047/api/v3'
36
+ authToken?: string; // PAT (Recommended)
37
+ username?: string; // For legacy auth
38
+ password?: string; // For legacy auth
39
+ checkSSLCerts?: boolean; // Set to false for self-signed certs (Node.js only)
40
+ timeout?: number; // Request timeout in ms
41
+ }
42
+ ```
43
+
44
+ ## SSL/TLS Configuration (Self-Signed Certs)
45
+
46
+ If you are connecting to a Dremio Software instance with a self-signed certificate, you may need to disable SSL verification in Node.js.
47
+
48
+ ```typescript
49
+ const client = new DremioSoftwareClient({
50
+ baseUrl: 'https://dremio.local:9047/api/v3',
51
+ authToken: '...',
52
+ checkSSLCerts: false // WARNING: Use only for development
53
+ });
54
+ ```
@@ -0,0 +1,41 @@
1
+ # Governance & Collaboration API Guide
2
+
3
+ ## Governance (Grants)
4
+
5
+ Manage access control lists (ACLs) and grants for Catalog entities.
6
+
7
+ ### Getting Grants
8
+ ```typescript
9
+ const grants = await client.governance.getGrants('entity-id');
10
+ // Output: [{ granteeType: 'USER', id: '...', privileges: ['SELECT'] }]
11
+ ```
12
+
13
+ ### Updating Grants
14
+ ```typescript
15
+ await client.governance.updateGrants('entity-id', [
16
+ { granteeType: 'ROLE', id: 'role-id', privileges: ['SELECT', 'ALTER'] }
17
+ ]);
18
+ ```
19
+
20
+ ## Collaboration (Tags & Wiki)
21
+
22
+ Manage metadata that helps users discover and understand data.
23
+
24
+ ### Tags
25
+ ```typescript
26
+ // Get Tags
27
+ const tags = await client.collaboration.getTags('dataset-id');
28
+
29
+ // Set Tags
30
+ await client.collaboration.setTags('dataset-id', ['marketing', 'q1-2024']);
31
+ ```
32
+
33
+ ### Wiki
34
+ ```typescript
35
+ // Get Wiki Markdown
36
+ const wiki = await client.collaboration.getWiki('dataset-id');
37
+ console.log(wiki.text);
38
+
39
+ // Set Wiki
40
+ await client.collaboration.setWiki('dataset-id', '# Sales Data\n\nThis dataset contains...');
41
+ ```
@@ -0,0 +1,15 @@
1
+ # Provisioning API Guide
2
+
3
+ Manage Dremio Engines (Cloud) or Queues (Software WLM).
4
+
5
+ ## Engines (Dremio Cloud)
6
+
7
+ ```typescript
8
+ // List available engines
9
+ const engines = await client.provisioning.listEngines();
10
+ engines.forEach(engine => {
11
+ console.log(`Engine: ${engine.name} (${engine.size}) - ${engine.status}`);
12
+ });
13
+ ```
14
+
15
+ *Note: Software WLM Queue management is structurally different and may be added to this namespace in future updates.*
@@ -0,0 +1,54 @@
1
+ # Reflections API Guide
2
+
3
+ Reflections are Dremio's query acceleration technology. This API allows you to manage raw and aggregation reflections programmatically.
4
+
5
+ ## Listing Reflections
6
+
7
+ You can list all reflections or filter by dataset.
8
+
9
+ ```typescript
10
+ // List all accessible reflections
11
+ const allReflections = await client.reflections.list();
12
+
13
+ // List reflections for a specific dataset
14
+ const datasetReflections = await client.reflections.list({
15
+ datasetId: 'dataset-uuid-123'
16
+ });
17
+ ```
18
+
19
+ ## Getting a Reflection
20
+
21
+ ```typescript
22
+ const reflection = await client.reflections.get('reflection-uuid');
23
+ console.log(reflection.name, reflection.status);
24
+ ```
25
+
26
+ ## Creating a Reflection
27
+
28
+ Creating a reflection implies defining which dataset it accelerates and its configuration (dimensions, measures, etc.).
29
+
30
+ ```typescript
31
+ await client.reflections.create({
32
+ type: 'RAW',
33
+ name: 'Raw_Acceleration',
34
+ datasetId: 'dataset-uuid-123',
35
+ enabled: true,
36
+ displayFieldIds: ['col1', 'col2'] // Fields to include
37
+ });
38
+ ```
39
+
40
+ ## Updating a Reflection
41
+
42
+ Commonly used to enable/disable reflections or update their definition.
43
+
44
+ ```typescript
45
+ await client.reflections.update('reflection-uuid', {
46
+ enabled: false // Disable reflection
47
+ });
48
+ ```
49
+
50
+ ## Deleting a Reflection
51
+
52
+ ```typescript
53
+ await client.reflections.delete('reflection-uuid');
54
+ ```
@@ -0,0 +1,45 @@
1
+ # Scripts, Lineage & Tokens Guide
2
+
3
+ ## Scripts API
4
+
5
+ Manage saved SQL scripts (Dremio Cloud & Software).
6
+
7
+ ```typescript
8
+ // List Scripts
9
+ const scripts = await client.scripts.list({ maxResults: 10 });
10
+
11
+ // Get a Script
12
+ const myScript = await client.scripts.get('script-id');
13
+
14
+ // Create a Script
15
+ const newScript = await client.scripts.create({
16
+ name: 'Daily Report',
17
+ content: 'SELECT count(*) FROM sales',
18
+ context: ['Marketing']
19
+ });
20
+
21
+ // Update
22
+ await client.scripts.update('script-id', { content: 'SELECT * FROM sales' });
23
+
24
+ // Delete
25
+ await client.scripts.delete('script-id');
26
+ ```
27
+
28
+ ## Lineage API
29
+
30
+ Explore the data lineage graph for a dataset.
31
+
32
+ ```typescript
33
+ const graph = await client.lineage.get('dataset-id');
34
+ console.log(graph);
35
+ // Returns nodes and edges representing parent/child relationships
36
+ ```
37
+
38
+ ## Token Management
39
+
40
+ Programmatically generate Personal Access Tokens (PATs) (where supported).
41
+
42
+ ```typescript
43
+ const tokenResponse = await client.tokens.createToken('user-id', 'My New Token');
44
+ console.log('New PAT:', tokenResponse.token);
45
+ ```
@@ -0,0 +1,77 @@
1
+ # Service Users & Credentials Guide
2
+
3
+ Managing non-human accounts (Service Users) is essential for CI/CD and automated workflows. dremiojs provides full lifecycle management for Service Users and their OAuth credentials.
4
+
5
+ > **Note**: Service Users are primarily a **Dremio Enterprise** feature. Calls to these endpoints may fail on Community Edition.
6
+
7
+ ## Service User Lifecycle
8
+
9
+ ### 1. Create a Service User
10
+
11
+ ```typescript
12
+ const serviceUser = await client.users.create({
13
+ name: 'ci_pipeline_bot',
14
+ identityType: 'SERVICE_USER',
15
+ active: true
16
+ });
17
+ console.log('Service User Created:', serviceUser.id);
18
+ ```
19
+
20
+ ### 2. Create Client Credentials (Secret)
21
+
22
+ Generate a Client ID and Secret for this user. This secret is only shown ONCE.
23
+
24
+ ```typescript
25
+ const credential = await client.credentials.create(
26
+ serviceUser.id,
27
+ 'gitlab-access-token',
28
+ 180 // Lifetime in days
29
+ );
30
+
31
+ console.log('Client ID:', credential.clientSecretConfig.clientId);
32
+ console.log('Client Secret:', credential.clientSecretConfig.clientSecret);
33
+ // SAVE THE SECRET NOW! It cannot be retrieved again.
34
+ ```
35
+
36
+ ### 3. List Credentials
37
+
38
+ View active credentials for a user.
39
+
40
+ ```typescript
41
+ const creds = await client.credentials.list(serviceUser.id);
42
+ creds.forEach(c => console.log(c.name, c.credentialType));
43
+ ```
44
+
45
+ ### 4. Delete Credentials
46
+
47
+ Revoke a specific credential.
48
+
49
+ ```typescript
50
+ await client.credentials.delete(serviceUser.id, credential.id);
51
+ ```
52
+
53
+ ### 5. Delete Service User
54
+
55
+ ```typescript
56
+ await client.users.delete(serviceUser.id, serviceUser.tag);
57
+ ```
58
+
59
+ ## Dremio Cloud Service Accounts
60
+
61
+ In Dremio Cloud, "Service Users" are typically handled as regular users with **Personal Access Tokens (PATs)**.
62
+
63
+ ### Creating a Token Programmatically
64
+
65
+ You can generate tokens for users (if you have permissions) using the `TokensResource`.
66
+
67
+ ```typescript
68
+ // Create a token for a specific user ID
69
+ const { token } = await client.tokens.createToken(
70
+ 'user-uuid-1234',
71
+ 'ci-pipeline-token',
72
+ 90 // Lifetime in days (default 30)
73
+ );
74
+
75
+ console.log('Generated PAT:', token);
76
+ ```
77
+
@@ -0,0 +1,53 @@
1
+ # Sources API Guide
2
+
3
+ Sources are the physical connections to your data lakes and databases (e.g., S3, HDFS, PostgreSQL, Snowflake).
4
+
5
+ ## Listing Sources
6
+
7
+ ```typescript
8
+ const sources = await client.sources.list();
9
+ sources.forEach(source => {
10
+ console.log(source.name, source.type); // e.g. "MyS3", "S3"
11
+ });
12
+ ```
13
+
14
+ ## Getting a Source
15
+
16
+ ```typescript
17
+ const source = await client.sources.get('source-id');
18
+ ```
19
+
20
+ ## Creating a Source
21
+
22
+ Creating a source requires a specific configuration object that varies by source type (S3, Nessie, etc.). *Note: The specific config payload structure depends on the Dremio version and source type.*
23
+
24
+ ```typescript
25
+ // Example: Creating an S3 Source
26
+ await client.sources.create({
27
+ name: 'MyS3Source',
28
+ type: 'S3',
29
+ config: {
30
+ bucketName: 'my-bucket',
31
+ accessKey: '...',
32
+ accessSecret: '...'
33
+ // ... other S3 specific props
34
+ }
35
+ });
36
+ ```
37
+
38
+ ## Updating a Source
39
+
40
+ ```typescript
41
+ await client.sources.update('source-id', {
42
+ config: {
43
+ ...oldConfig,
44
+ accessSecret: 'NEW_SECRET'
45
+ }
46
+ });
47
+ ```
48
+
49
+ ## Deleting a Source
50
+
51
+ ```typescript
52
+ await client.sources.delete('source-id');
53
+ ```
@@ -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.1.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"
@@ -22,4 +26,4 @@
22
26
  "ts-node": "^10.9.2",
23
27
  "typescript": "^5.9.3"
24
28
  }
25
- }
29
+ }
@@ -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> {
@@ -34,4 +34,8 @@ export class DremioCloudClient extends BaseClient {
34
34
  getProjectId(): string {
35
35
  return this.config.projectId;
36
36
  }
37
+
38
+ public getConfig(): DremioCloudConfig {
39
+ return this.config;
40
+ }
37
41
  }
@@ -39,6 +39,22 @@ const runTests = async () => {
39
39
  console.warn(' Warning: Could not list reflections:', e.message);
40
40
  }
41
41
 
42
+ console.log(' - Validating Provisioning (Engines)...');
43
+ try {
44
+ const engines = await cloudClient.provisioning.listEngines();
45
+ console.log(' Success! Found', engines.length, 'engines');
46
+ } catch (e: any) {
47
+ console.warn(' Warning: Could not list engines:', e.message);
48
+ }
49
+
50
+ console.log(' - Validating Scripts...');
51
+ try {
52
+ const scripts = await cloudClient.scripts.list();
53
+ console.log(' Success! Found', scripts.length, 'scripts');
54
+ } catch (e: any) {
55
+ console.warn(' Warning: Could not list scripts:', e.message);
56
+ }
57
+
42
58
  console.log(' - Running Test Query: SELECT 1');
43
59
  const results = await cloudClient.jobs.executeQuery('SELECT 1');
44
60
  console.log(' Query Success! Result:', results);
@@ -80,6 +96,39 @@ const runTests = async () => {
80
96
  console.warn(' Warning: Could not list reflections:', e.message);
81
97
  }
82
98
 
99
+ console.log(' - Validating Scripts...');
100
+ try {
101
+ const scripts = await softwareClient.scripts.list();
102
+ console.log(' Success! Found', scripts.length, 'scripts');
103
+ } catch (e: any) {
104
+ console.warn(' Warning: Could not list scripts (Check if supported/enabled in this version):', e.message);
105
+ }
106
+
107
+ console.log(' - Validating Service User Lifecycle...');
108
+ try {
109
+ // 1. Create Service User
110
+ const userName = `test_svc_${Date.now()}`;
111
+ console.log(` Creating Service User: ${userName}...`);
112
+ const newUser = await softwareClient.users.create({
113
+ name: userName,
114
+ identityType: 'SERVICE_USER'
115
+ });
116
+ console.log(' Success! Created User ID:', newUser.id);
117
+
118
+ // 2. Create Credential
119
+ console.log(' Creating Credential...');
120
+ const cred = await softwareClient.credentials.create(newUser.id, 'test-secret', 30);
121
+ console.log(' Success! Created Credential ID:', cred.id);
122
+
123
+ // 3. Delete User
124
+ console.log(' Deleting User...');
125
+ await softwareClient.users.delete(newUser.id, newUser.tag);
126
+ console.log(' Success! User Deleted.');
127
+
128
+ } catch (e: any) {
129
+ console.warn(' Warning: Service User test failed (Admin privileges required?):', e.message);
130
+ }
131
+
83
132
  console.log(' - Running Test Query: SELECT 1');
84
133
  const results = await softwareClient.jobs.executeQuery('SELECT 1');
85
134
  console.log(' Query Success! Result:', results);