@pipedream/tableau 0.1.0 → 0.3.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 ADDED
@@ -0,0 +1,17 @@
1
+ # Overview
2
+
3
+ The Tableau API allows you to tap into the robust data visualization and business intelligence capabilities of Tableau. Within Pipedream, you can leverage this API to automate reporting, manage users, update data sources, and extract insights. This enables you to integrate Tableau's analytics with other services, streamlining your data workflows and ensuring your dashboards remain up-to-date with minimal manual effort.
4
+
5
+ # Example Use Cases
6
+
7
+ - **Automated Snapshot Sharing**: Generate snapshots of your key Tableau dashboards and share them via email or Slack at regular intervals. This keeps your team informed with the latest business insights without manual exports.
8
+
9
+ _Example Workflow_: Trigger a Pipedream workflow on a schedule; use Tableau API to capture a view of the dashboard; send the image via Gmail or post to a Slack channel using their respective Pipedream app integrations.
10
+
11
+ - **Dynamic Data Updates**: Automatically update Tableau data sources when new data comes into your backend systems, ensuring that Tableau dashboards reflect the most current data.
12
+
13
+ _Example Workflow_: Trigger a Pipedream workflow with a webhook when new data is added to a database; process and format the data within the workflow; use Tableau API to refresh the corresponding data source on Tableau Server or Tableau Online.
14
+
15
+ - **User Management Automation**: Streamline user provisioning by automating the addition and removal of users to and from Tableau sites based on HR software triggers or internal databases.
16
+
17
+ _Example Workflow_: Trigger a Pipedream workflow from a user management event in an app like BambooHR; use conditions within the workflow to determine if the user should be added or removed; utilize Tableau API to update the user list on the relevant Tableau site.
@@ -4,7 +4,12 @@ export default {
4
4
  key: "tableau-create-project",
5
5
  name: "Create Project",
6
6
  description: "Creates a project on the specified site. You can also create project hierarchies by creating a project under the specified parent project on the site. [See the documentation](https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref_projects.htm#create_project)",
7
- version: "0.0.1",
7
+ version: "0.0.4",
8
+ annotations: {
9
+ destructiveHint: false,
10
+ openWorldHint: true,
11
+ readOnlyHint: false,
12
+ },
8
13
  type: "action",
9
14
  props: {
10
15
  app,
@@ -0,0 +1,135 @@
1
+ import app from "../../tableau.app.mjs";
2
+ import fs from "fs";
3
+ import path from "path";
4
+
5
+ export default {
6
+ key: "tableau-download-pdf",
7
+ name: "Download PDF",
8
+ description: "Downloads images of the sheets of a workbook as a PDF file. [See the documentation](https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref_workbooks_and_views.htm#download_workbook_pdf)",
9
+ version: "0.0.3",
10
+ annotations: {
11
+ destructiveHint: false,
12
+ openWorldHint: true,
13
+ readOnlyHint: false,
14
+ },
15
+ type: "action",
16
+ props: {
17
+ app,
18
+ siteId: {
19
+ propDefinition: [
20
+ app,
21
+ "siteId",
22
+ ],
23
+ },
24
+ workbookId: {
25
+ propDefinition: [
26
+ app,
27
+ "workbookId",
28
+ ({ siteId }) => ({
29
+ siteId,
30
+ }),
31
+ ],
32
+ },
33
+ maxAge: {
34
+ type: "integer",
35
+ label: "Max Age",
36
+ description: "The maximum number of minutes a workbook PDF will be cached before being refreshed. Minimum interval is one minute",
37
+ optional: true,
38
+ min: 1,
39
+ },
40
+ orientation: {
41
+ type: "string",
42
+ label: "Page Orientation",
43
+ description: "The orientation of the pages in the PDF file produced",
44
+ options: [
45
+ "Portrait",
46
+ "Landscape",
47
+ ],
48
+ default: "Portrait",
49
+ optional: true,
50
+ },
51
+ pageType: {
52
+ type: "string",
53
+ label: "Page Type",
54
+ description: "The type of page, which determines the page dimensions of the PDF file returned",
55
+ options: [
56
+ "A3",
57
+ "A4",
58
+ "A5",
59
+ "B5",
60
+ "Executive",
61
+ "Folio",
62
+ "Ledger",
63
+ "Legal",
64
+ "Letter",
65
+ "Note",
66
+ "Quarto",
67
+ "Tabloid",
68
+ ],
69
+ default: "Legal",
70
+ optional: true,
71
+ },
72
+ vizHeight: {
73
+ type: "integer",
74
+ label: "Viz Height",
75
+ description: "The height of the rendered PDF image in pixels that, along with `Viz Width`, determines its resolution and aspect ratio",
76
+ optional: true,
77
+ },
78
+ vizWidth: {
79
+ type: "integer",
80
+ label: "Viz Width",
81
+ description: "The width of the rendered PDF image in pixels that, along with `Viz Height`, determines its resolution and aspect ratio",
82
+ optional: true,
83
+ },
84
+ outputFilename: {
85
+ type: "string",
86
+ label: "Output Filename",
87
+ description: "The filename for the downloaded PDF file, which will be saved to the `/tmp` folder",
88
+ default: "workbook.pdf",
89
+ optional: true,
90
+ },
91
+ syncDir: {
92
+ type: "dir",
93
+ accessMode: "write",
94
+ sync: true,
95
+ },
96
+ },
97
+ async run({ $ }) {
98
+ const {
99
+ siteId,
100
+ workbookId,
101
+ maxAge,
102
+ orientation,
103
+ pageType,
104
+ vizHeight,
105
+ vizWidth,
106
+ outputFilename,
107
+ } = this;
108
+
109
+ const response = await this.app.downloadWorkbookPdf({
110
+ $,
111
+ siteId,
112
+ workbookId,
113
+ params: {
114
+ "max-age-minutes": maxAge,
115
+ orientation,
116
+ "page-type": pageType,
117
+ "viz-height": vizHeight,
118
+ "viz-width": vizWidth,
119
+ },
120
+ responseType: "arraybuffer",
121
+ });
122
+
123
+ // Write the PDF to the /tmp folder
124
+ const filename = outputFilename || "workbook.pdf";
125
+ const filePath = path.join("/tmp", filename);
126
+
127
+ await fs.promises.writeFile(filePath, Buffer.from(response));
128
+
129
+ $.export("$summary", `Successfully downloaded workbook PDF to \`${filePath}\``);
130
+ return {
131
+ filePath,
132
+ fileContent: response,
133
+ };
134
+ },
135
+ };
@@ -0,0 +1,71 @@
1
+ import tableau from "../../tableau.app.mjs";
2
+ import { ConfigurationError } from "@pipedream/platform";
3
+
4
+ export default {
5
+ key: "tableau-list-sites",
6
+ name: "List Sites",
7
+ description: "Returns a list of the sites on the server that the caller of this method has access to. [See the documentation](https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref_site.htm#query_sites)",
8
+ version: "0.0.1",
9
+ type: "action",
10
+ annotations: {
11
+ destructiveHint: false,
12
+ openWorldHint: true,
13
+ readOnlyHint: true,
14
+ },
15
+ props: {
16
+ tableau,
17
+ info: {
18
+ type: "alert",
19
+ alertType: "info",
20
+ content: "Listing all sites requires Server Administrator Permissions. If you don't have these permissions, the action will return the site from the current session.",
21
+ },
22
+ pageSize: {
23
+ type: "integer",
24
+ label: "Page Size",
25
+ description: "The number of sites to return per page. The default is 100. Maximum is 1000. Only used for calls with Server Administrator Permissions.",
26
+ optional: true,
27
+ default: 100,
28
+ min: 1,
29
+ max: 1000,
30
+ },
31
+ pageNumber: {
32
+ type: "integer",
33
+ label: "Page Number",
34
+ description: "The page number to return. The default is 1. Only used for calls with Server Administrator Permissions.",
35
+ optional: true,
36
+ default: 1,
37
+ min: 1,
38
+ },
39
+ },
40
+ async run({ $ }) {
41
+ let response;
42
+ try {
43
+ response = await this.tableau.listSites({
44
+ $,
45
+ params: {
46
+ pageSize: this.pageSize,
47
+ pageNumber: this.pageNumber,
48
+ },
49
+ });
50
+ } catch (error) {
51
+ if (error?.message?.includes("Invalid page number")) {
52
+ throw new ConfigurationError("Page number invalid.");
53
+ }
54
+ // User lacks permission to list sites. Return the site from the current session.
55
+ const { session: { site } } = await this.tableau.getCurrentSession({
56
+ $,
57
+ });
58
+ response = {
59
+ sites: {
60
+ site: [
61
+ site,
62
+ ],
63
+ },
64
+ };
65
+ }
66
+ $.export("$summary", `Successfully listed ${response.sites.site.length} site${response.sites.site.length === 1
67
+ ? ""
68
+ : "s"}`);
69
+ return response;
70
+ },
71
+ };
@@ -0,0 +1,85 @@
1
+ import tableau from "../../tableau.app.mjs";
2
+ import { ConfigurationError } from "@pipedream/platform";
3
+
4
+ export default {
5
+ key: "tableau-list-workbooks",
6
+ name: "List Workbooks",
7
+ description: "Returns a list of the workbooks on the specified site. [See the documentation](https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref_workbooks_and_views.htm#query_workbooks_for_site)",
8
+ version: "0.0.1",
9
+ type: "action",
10
+ annotations: {
11
+ destructiveHint: false,
12
+ openWorldHint: true,
13
+ readOnlyHint: true,
14
+ },
15
+ props: {
16
+ tableau,
17
+ siteId: {
18
+ description: "The ID of the site to list workbooks for. Can use the **List Sites** action to get a list of sites.",
19
+ propDefinition: [
20
+ tableau,
21
+ "siteId",
22
+ ],
23
+ },
24
+ filter: {
25
+ type: "string",
26
+ label: "Filter",
27
+ description: "An expression that lets you specify a subset of workbooks to return. You can filter on predefined fields such as name, tags, and createdAt. You can include multiple filter expressions. For more information, see [Filtering and Sorting](https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_concepts_filtering_and_sorting.htm).",
28
+ optional: true,
29
+ },
30
+ sort: {
31
+ type: "string",
32
+ label: "Sort",
33
+ description: "An expression that lets you specify the order in which workbook information is returned. If you do not specify a sort expression, the sort order of the information that's returned is undefined. For more information, see [Filtering and Sorting](https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_concepts_filtering_and_sorting.htm).",
34
+ optional: true,
35
+ },
36
+ field: {
37
+ type: "string",
38
+ label: "Field",
39
+ description: "An expression that lets you specify the set of available fields to return. You can qualify the return values based upon predefined keywords such as _all_ or _default_, and you can specify individual fields for the workbooks or other supported resources. You can include multiple field expressions in a request. For more information, see [Using Fields in the REST API](https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_concepts_fields.htm).",
40
+ optional: true,
41
+ },
42
+ pageSize: {
43
+ type: "integer",
44
+ label: "Page Size",
45
+ description: "The number of workbooks to return per page. The default is 100. Maximum is 1000.",
46
+ optional: true,
47
+ default: 100,
48
+ min: 1,
49
+ max: 1000,
50
+ },
51
+ pageNumber: {
52
+ type: "integer",
53
+ label: "Page Number",
54
+ description: "The page number to return. The default is 1.",
55
+ optional: true,
56
+ default: 1,
57
+ min: 1,
58
+ },
59
+ },
60
+ async run({ $ }) {
61
+ let response;
62
+ try {
63
+ response = await this.tableau.listWorkbooks({
64
+ $,
65
+ siteId: this.siteId,
66
+ params: {
67
+ filter: this.filter,
68
+ sort: this.sort,
69
+ field: this.field,
70
+ pageSize: this.pageSize,
71
+ pageNumber: this.pageNumber,
72
+ },
73
+ });
74
+ } catch (error) {
75
+ if (error?.message?.includes("Invalid page number")) {
76
+ throw new ConfigurationError("Page number invalid.");
77
+ }
78
+ }
79
+ const count = response.workbooks?.workbook?.length ?? 0;
80
+ $.export("$summary", `Successfully listed ${count} workbook${count === 1
81
+ ? ""
82
+ : "s"}`);
83
+ return response;
84
+ },
85
+ };
@@ -5,7 +5,12 @@ export default {
5
5
  key: "tableau-query-projects",
6
6
  name: "Query Projects",
7
7
  description: "Returns a list of projects on the specified site. [See the documentation](https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref_projects.htm)",
8
- version: "0.0.1",
8
+ version: "0.0.4",
9
+ annotations: {
10
+ destructiveHint: false,
11
+ openWorldHint: true,
12
+ readOnlyHint: true,
13
+ },
9
14
  type: "action",
10
15
  props: {
11
16
  app,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pipedream/tableau",
3
- "version": "0.1.0",
3
+ "version": "0.3.0",
4
4
  "description": "Pipedream Tableau Components",
5
5
  "main": "tableau.app.mjs",
6
6
  "keywords": [
@@ -13,6 +13,6 @@
13
13
  "access": "public"
14
14
  },
15
15
  "dependencies": {
16
- "@pipedream/platform": "^1.6.0"
16
+ "@pipedream/platform": "^1.6.8"
17
17
  }
18
18
  }
@@ -6,7 +6,7 @@ export default {
6
6
  key: "tableau-label-created-instant",
7
7
  name: "New Label Created (Instant)",
8
8
  description: "Emit new event when a label is created in Tableau. [See the documentation](https://help.tableau.com/current/developer/webhooks/en-us/docs/webhooks-events-payload.html)",
9
- version: "0.0.1",
9
+ version: "0.0.3",
10
10
  type: "source",
11
11
  dedupe: "unique",
12
12
  methods: {
@@ -6,7 +6,7 @@ export default {
6
6
  key: "tableau-workbook-created-instant",
7
7
  name: "New Workbook Created (Instant)",
8
8
  description: "Emit new event each time a new workbook is created in Tableau. [See the documentation](https://help.tableau.com/current/developer/webhooks/en-us/docs/webhooks-events-payload.html)",
9
- version: "0.0.1",
9
+ version: "0.0.3",
10
10
  type: "source",
11
11
  dedupe: "unique",
12
12
  methods: {
package/tableau.app.mjs CHANGED
@@ -47,6 +47,32 @@ export default {
47
47
  }));
48
48
  },
49
49
  },
50
+ workbookId: {
51
+ type: "string",
52
+ label: "Workbook ID",
53
+ description: "The ID of the workbook to download as PDF",
54
+ async options({
55
+ siteId, page,
56
+ }) {
57
+ if (!siteId) {
58
+ return [];
59
+ }
60
+ const { workbooks: { workbook: data } } =
61
+ await this.listWorkbooks({
62
+ siteId,
63
+ params: {
64
+ pageSize: constants.DEFAULT_LIMIT,
65
+ pageNumber: page + 1,
66
+ },
67
+ });
68
+ return data?.map(({
69
+ id: value, name: label,
70
+ }) => ({
71
+ label,
72
+ value,
73
+ })) || [];
74
+ },
75
+ },
50
76
  },
51
77
  methods: {
52
78
  getUrl(path) {
@@ -95,6 +121,28 @@ export default {
95
121
  ...args,
96
122
  });
97
123
  },
124
+ listWorkbooks({
125
+ siteId, ...args
126
+ }) {
127
+ return this._makeRequest({
128
+ path: `/sites/${siteId}/workbooks`,
129
+ ...args,
130
+ });
131
+ },
132
+ listSites(args = {}) {
133
+ return this._makeRequest({
134
+ path: "/sites",
135
+ ...args,
136
+ });
137
+ },
138
+ downloadWorkbookPdf({
139
+ siteId, workbookId, ...args
140
+ }) {
141
+ return this._makeRequest({
142
+ path: `/sites/${siteId}/workbooks/${workbookId}/pdf`,
143
+ ...args,
144
+ });
145
+ },
98
146
  async *getIterations({
99
147
  resourcesFn, resourcesFnArgs, resourceName,
100
148
  max = constants.DEFAULT_MAX,