@sinehypernova-0718/n8n-nodes-pinterest 0.1.0 → 0.1.2

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.
Files changed (69) hide show
  1. package/README.md +142 -26
  2. package/dist/n8n-logo-black-accent.svg +17 -0
  3. package/dist/nodes/Pinterest/Pinterest.node.js +217 -28
  4. package/dist/nodes/Pinterest/Pinterest.node.js.map +1 -1
  5. package/dist/nodes/Pinterest/Pinterest.node.json +1 -1
  6. package/dist/nodes/Pinterest/constants.d.ts +1 -0
  7. package/dist/nodes/Pinterest/constants.js +5 -0
  8. package/dist/nodes/Pinterest/constants.js.map +1 -0
  9. package/dist/nodes/Pinterest/descriptions/BoardDescriptions.js +318 -2
  10. package/dist/nodes/Pinterest/descriptions/BoardDescriptions.js.map +1 -1
  11. package/dist/nodes/Pinterest/descriptions/MediaDescriptions.d.ts +2 -0
  12. package/dist/nodes/Pinterest/descriptions/MediaDescriptions.js +168 -0
  13. package/dist/nodes/Pinterest/descriptions/MediaDescriptions.js.map +1 -0
  14. package/dist/nodes/Pinterest/descriptions/PinDescriptions.js +221 -131
  15. package/dist/nodes/Pinterest/descriptions/PinDescriptions.js.map +1 -1
  16. package/dist/nodes/Pinterest/descriptions/index.d.ts +1 -0
  17. package/dist/nodes/Pinterest/descriptions/index.js +3 -1
  18. package/dist/nodes/Pinterest/descriptions/index.js.map +1 -1
  19. package/dist/nodes/Pinterest/requests/board/createSection.d.ts +2 -0
  20. package/dist/nodes/Pinterest/requests/board/createSection.js +54 -0
  21. package/dist/nodes/Pinterest/requests/board/createSection.js.map +1 -0
  22. package/dist/nodes/Pinterest/requests/board/deleteSection.d.ts +2 -0
  23. package/dist/nodes/Pinterest/requests/board/deleteSection.js +52 -0
  24. package/dist/nodes/Pinterest/requests/board/deleteSection.js.map +1 -0
  25. package/dist/nodes/Pinterest/requests/board/index.d.ts +5 -0
  26. package/dist/nodes/Pinterest/requests/board/index.js +11 -1
  27. package/dist/nodes/Pinterest/requests/board/index.js.map +1 -1
  28. package/dist/nodes/Pinterest/requests/board/listSectionPins.d.ts +2 -0
  29. package/dist/nodes/Pinterest/requests/board/listSectionPins.js +73 -0
  30. package/dist/nodes/Pinterest/requests/board/listSectionPins.js.map +1 -0
  31. package/dist/nodes/Pinterest/requests/board/listSections.d.ts +2 -0
  32. package/dist/nodes/Pinterest/requests/board/listSections.js +62 -0
  33. package/dist/nodes/Pinterest/requests/board/listSections.js.map +1 -0
  34. package/dist/nodes/Pinterest/requests/board/updateSection.d.ts +2 -0
  35. package/dist/nodes/Pinterest/requests/board/updateSection.js +67 -0
  36. package/dist/nodes/Pinterest/requests/board/updateSection.js.map +1 -0
  37. package/dist/nodes/Pinterest/requests/index.d.ts +3 -2
  38. package/dist/nodes/Pinterest/requests/index.js +16 -1
  39. package/dist/nodes/Pinterest/requests/index.js.map +1 -1
  40. package/dist/nodes/Pinterest/requests/media/get.d.ts +2 -0
  41. package/dist/nodes/Pinterest/requests/media/get.js +26 -0
  42. package/dist/nodes/Pinterest/requests/media/get.js.map +1 -0
  43. package/dist/nodes/Pinterest/requests/media/index.d.ts +4 -0
  44. package/dist/nodes/Pinterest/requests/media/index.js +13 -0
  45. package/dist/nodes/Pinterest/requests/media/index.js.map +1 -0
  46. package/dist/nodes/Pinterest/requests/media/list.d.ts +2 -0
  47. package/dist/nodes/Pinterest/requests/media/list.js +38 -0
  48. package/dist/nodes/Pinterest/requests/media/list.js.map +1 -0
  49. package/dist/nodes/Pinterest/requests/media/multipart.d.ts +11 -0
  50. package/dist/nodes/Pinterest/requests/media/multipart.js +49 -0
  51. package/dist/nodes/Pinterest/requests/media/multipart.js.map +1 -0
  52. package/dist/nodes/Pinterest/requests/media/normalize.d.ts +7 -0
  53. package/dist/nodes/Pinterest/requests/media/normalize.js +49 -0
  54. package/dist/nodes/Pinterest/requests/media/normalize.js.map +1 -0
  55. package/dist/nodes/Pinterest/requests/media/upload.d.ts +2 -0
  56. package/dist/nodes/Pinterest/requests/media/upload.js +321 -0
  57. package/dist/nodes/Pinterest/requests/media/upload.js.map +1 -0
  58. package/dist/nodes/Pinterest/requests/media/validateUploadUrl.d.ts +5 -0
  59. package/dist/nodes/Pinterest/requests/media/validateUploadUrl.js +48 -0
  60. package/dist/nodes/Pinterest/requests/media/validateUploadUrl.js.map +1 -0
  61. package/dist/nodes/Pinterest/requests/pin/index.d.ts +1 -0
  62. package/dist/nodes/Pinterest/requests/pin/index.js +6 -1
  63. package/dist/nodes/Pinterest/requests/pin/index.js.map +1 -1
  64. package/dist/nodes/Pinterest/requests/pin/productTags.d.ts +5 -0
  65. package/dist/nodes/Pinterest/requests/pin/productTags.js +109 -0
  66. package/dist/nodes/Pinterest/requests/pin/productTags.js.map +1 -0
  67. package/dist/package.json +2 -1
  68. package/dist/tsconfig.tsbuildinfo +1 -1
  69. package/package.json +2 -1
package/README.md CHANGED
@@ -1,5 +1,5 @@
1
1
  <p align="center">
2
- <img src="https://raw.githubusercontent.com/n8n-io/n8n/master/assets/n8n-logo.png" width="200" alt="n8n" />
2
+ <img src="n8n-logo-black-accent.svg" width="200" alt="n8n" />
3
3
  </p>
4
4
 
5
5
  <h1 align="center">n8n-nodes-pinterest</h1>
@@ -14,19 +14,19 @@
14
14
 
15
15
  [n8n](https://n8n.io/) is a [fair-code licensed](https://docs.n8n.io/reference/license/) workflow automation platform.
16
16
 
17
- **n8n-nodes-pinterest** brings Pinterest API operations into n8n workflows, letting you work with Pinterest resources through the workflow editor instead of manually constructing API requests.
17
+ **@sinehypernova-0718/n8n-nodes-pinterest** brings Pinterest API operations into n8n workflows, letting you work with Pinterest resources through the workflow editor instead of manually constructing API requests.
18
18
 
19
19
  ## ✨ Features
20
20
 
21
- - **Pinterest API integration** — interact with supported Pinterest resources and operations directly from n8n workflows.
22
- - **OAuth2 authentication** — authenticate requests using Pinterest OAuth2 credentials.
23
- - **Native n8n interface** — select resources and operations and configure their parameters through the workflow editor.
24
- - **Pin management** — create, retrieve, list, update, delete, and save Pins using supported Pinterest API operations.
25
- - **Board management** — create, retrieve, list, update, and delete boards, and retrieve Pins associated with a board.
26
- - **Pin analytics** — retrieve analytics for supported metrics and date ranges.
27
- - **Pagination support** — retrieve paginated API results where supported by an operation.
28
- - **Modular TypeScript architecture** — resource descriptions, request builders, and execution logic are organized by responsibility.
29
- - **Extensible design** — add further Pinterest resources and operations while preserving the existing architecture.
21
+ - **Pinterest API v5 integration** — interact with supported Pinterest resources directly from n8n workflows.
22
+ - **OAuth2 authentication** — authorize API requests using Pinterest OAuth2 credentials.
23
+ - **Native n8n interface** — select resources, operations, and parameters through the workflow editor.
24
+ - **Pin management** — create, retrieve, list, update, delete, and save Pins, and manage product-tag associations.
25
+ - **Board management** — create, retrieve, list, update, and delete boards and board sections, and retrieve Pins associated with boards.
26
+ - **Pin analytics** — retrieve supported analytics metrics over specified date ranges.
27
+ - **Media uploads** — register, transfer, and track video uploads for video Pins, with optional processing polling.
28
+ - **Pagination support** — retrieve paginated results where supported by the relevant operation.
29
+
30
30
 
31
31
  ## 📁 Repository Structure
32
32
 
@@ -36,14 +36,22 @@ n8n-nodes-pinterest/
36
36
  │ └── PinterestOAuth2Api.credentials.ts
37
37
  ├── nodes/
38
38
  │ └── Pinterest/
39
+ │ ├── constants.ts
39
40
  │ ├── descriptions/
40
41
  │ │ ├── BoardDescriptions.ts
42
+ │ │ ├── MediaDescriptions.ts
41
43
  │ │ ├── PinDescriptions.ts
42
44
  │ │ └── index.ts
43
45
  │ ├── requests/
44
46
  │ │ ├── board/
45
47
  │ │ │ ├── <operation>.ts
46
48
  │ │ │ └── index.ts
49
+ │ │ ├── media/
50
+ │ │ │ ├── <operation>.ts
51
+ │ │ │ ├── multipart.ts
52
+ │ │ │ ├── normalize.ts
53
+ │ │ │ ├── validateUploadUrl.ts
54
+ │ │ │ └── index.ts
47
55
  │ │ ├── pin/
48
56
  │ │ │ ├── <operation>.ts
49
57
  │ │ │ └── index.ts
@@ -57,15 +65,17 @@ n8n-nodes-pinterest/
57
65
  ### Architecture
58
66
 
59
67
  - **`credentials/`** — defines the Pinterest OAuth2 credentials used to authenticate requests.
68
+ - **`constants.ts`** — defines shared Pinterest API configuration, such as the API base URL.
60
69
  - **`descriptions/`** — contains resource and operation definitions displayed in the n8n editor. Its `index.ts` explicitly exports the descriptions consumed by the main node.
61
70
  - **`requests/board/`** — contains request builders for Pinterest Board operations.
71
+ - **`requests/media/`** — contains the Media request builders plus the multipart encoder, upload URL validation, and response normalization used by the video upload flow.
62
72
  - **`requests/pin/`** — contains request builders for Pinterest Pin operations.
63
73
  - **`requests/<resource>/<operation>.ts`** — keeps individual API request builders in separate operation-specific modules, such as `create.ts`, `get.ts`, `list.ts`, and `update.ts`.
64
74
  - **Resource `index.ts` files** — explicitly export the request builders belonging to each resource.
65
75
  - **`requests/index.ts`** — re-exports the resource-level barrels, providing a single import entry point for request modules.
66
- - **`Pinterest.node.ts`** — defines the n8n node and coordinates operation execution, authenticated HTTP requests, pagination, and error handling.
76
+ - **`Pinterest.node.ts`** — defines the n8n node and coordinates operation execution, authenticated API requests, media storage transfers, pagination, and error handling.
67
77
 
68
- The architecture separates editor-facing configuration from API request construction and execution. Resource descriptions define the available operations and parameters, while request modules build operation-specific requests. The main node remains responsible for invoking authenticated HTTP requests through n8n's credential-aware helpers and handling execution results.
78
+ The architecture separates editor-facing configuration from API request construction and execution. Resource descriptions define the available operations and parameters, while request modules build operation-specific requests. The main node remains responsible for invoking authenticated HTTP requests through n8n's credential-aware helpers, transferring media files to the storage URL returned by Pinterest, and handling execution results.
69
79
 
70
80
  Explicit exports keep imports predictable and avoid unnecessary wildcard exports. The resource-specific modules remain independent, while the barrel files provide convenient entry points without introducing another execution layer.
71
81
 
@@ -96,27 +106,53 @@ The Pin resource supports operations for managing Pins and retrieving their asso
96
106
 
97
107
  | Operation | Description |
98
108
  |---|---|
109
+ | Add Product Tags | Add product Pins as tags to a hero Pin. |
99
110
  | Create | Create a Pin using the supported Pinterest API parameters. |
100
111
  | Get | Retrieve an individual Pin by its ID. |
101
112
  | Get Multiple Pin Analytics | Retrieve analytics for multiple Pins in a batch request, using the supported analytics metrics and date range. |
102
113
  | List | List Pins with supported filters and pagination. |
114
+ | List Product Tags | List the product-tag associations on a hero Pin. |
103
115
  | Update | Update supported fields of an existing Pin. |
104
116
  | Delete | Delete a Pin by its ID. |
117
+ | Remove Product Tags | Remove product-tag associations from a hero Pin without deleting Pins or products. |
105
118
  | Save | Save a Pin to a board. |
106
119
  | Get Analytics | Retrieve analytics for a Pin using supported metrics and date parameters. |
107
120
 
108
- ### Boards
121
+ **Product tags** attach product Pins to a hero Pin. **Add Product Tags** and **Remove Product Tags** each accept a hero Pin ID and a comma-separated list of 1–24 product Pin IDs per request. These inputs are Pin IDs, not catalog item IDs. For Add Product Tags, Pinterest rejects duplicate `pin_id` values and applies eligibility checks; if any product Pin is ineligible, the entire request fails (all-or-nothing). Adding a Pin that is already tagged is a no-op success. **List Product Tags** returns the Pin IDs currently tagged to the hero Pin, or an empty array if there are none, and is not paginated. **Remove Product Tags** removes only the tag associations and returns HTTP 204 with no response body on success. Removing a tag that is not present is a no-op success. It does not delete the hero Pin, the product Pins, or any catalog products.
109
122
 
110
- The Board resource supports operations for managing boards and accessing their associated Pins.
123
+ Pinterest documents these `IneligibleProductTagReason` values for Add Product Tags: `PIN_MISSING`, `PIN_IS_PRIVATE`, `PRODUCT_METADATA_MISSING`, `PIN_NOT_FROM_VERIFIED_DOMAIN`, and `PIN_NOT_FROM_SAME_USER_AS_HERO_PIN`. A failed eligibility check returns HTTP 400 with the affected `pin_id` and its `error_message`.
124
+
125
+ ### Board
111
126
 
112
127
  | Operation | Description |
113
128
  |---|---|
114
- | Create | Create a Pinterest board. |
115
- | Get | Retrieve an individual board by its ID. |
116
- | List | List boards with pagination. |
117
- | Update | Update supported fields of an existing board. |
118
- | Delete | Delete a board by its ID. |
119
- | List Pins | Retrieve Pins associated with a board. |
129
+ | Create | Create a new board. |
130
+ | Create Board Section | Create a new board section. |
131
+ | Delete | Delete a single board. |
132
+ | Delete Board Section | Delete a single board section. |
133
+ | Get | Get a single board. |
134
+ | List | List boards. |
135
+ | List Board Sections | List sections of a board. |
136
+ | List Pins | List Pins on a board. |
137
+ | List Pins on Board Section | List Pins on a board section. |
138
+ | Update | Update a single board. |
139
+ | Update Board Section | Update a single board section. |
140
+
141
+ ### Media
142
+
143
+ The Media resource supports Pinterest's media upload endpoints for video Pins. Image uploads and Media Library operations are not implemented.
144
+
145
+ | Operation | Description |
146
+ |---|---|
147
+ | Get Upload | Retrieve the status of a media upload by its numeric media ID. |
148
+ | List Uploads | List media uploads with bookmark pagination. |
149
+ | Upload Video | Register a video upload, transfer the file to Pinterest media storage, and optionally wait for processing. |
150
+
151
+ **Upload Video** reads a video from an n8n binary property and supports MP4, M4V, and MOV files up to 512 MB. The node registers the upload, sends the file directly to Pinterest's media storage, and then checks the upload status. When **Wait for Processing** is enabled, the node polls Pinterest until the video finishes processing, fails, or the configured timeout is reached. The upload URL and temporary upload credentials returned by Pinterest are never included in node output or error messages.
152
+
153
+ Uploading a video does not create a Pin by itself. Use the returned `media_id` with the Pin resource to create a video Pin. Media uploads use the existing `pins:read` and `pins:write` scopes; no additional credential scopes are required.
154
+
155
+ The **List** operation for Pins supports **Return All** and **Limit**. The board listing operations (List, List Pins, List Board Sections, and List Pins on Board Section) follow Pinterest's pagination bookmarks and return all pages. **List Uploads** follows bookmark pagination and supports **Return All** and **Limit**. Pin, board, board section, and media IDs are numeric identifiers; operations that look up a resource by ID expect digits only.
120
156
 
121
157
  Operation parameters, filters, pagination behavior, and available analytics metrics vary by endpoint. Refer to the operation descriptions in the n8n editor and the [Pinterest API v5 documentation](https://developers.pinterest.com/docs/api/v5/) for endpoint-specific requirements.
122
158
 
@@ -156,7 +192,7 @@ Save the credential and use n8n's authorization flow to connect your Pinterest a
156
192
 
157
193
  ### Permissions and access
158
194
 
159
- The configured scopes cover the board and Pin read/write permissions used by the current implementation. The `user_accounts:read` scope supports the credential check against the `GET /v5/user_account` endpoint. Individual operations may still require specific permissions, resource access, or additional API eligibility.
195
+ The configured scopes cover the board, Pin, media upload, and product-tag operations used by the current implementation. The `user_accounts:read` scope supports the credential check against the `GET /v5/user_account` endpoint. Individual operations may still require specific permissions, resource access, or additional API eligibility.
160
196
 
161
197
  Pin analytics availability and supported metrics are subject to Pinterest's API requirements. Application approval status, access restrictions, and Pinterest-side changes can affect whether an operation succeeds.
162
198
 
@@ -170,14 +206,57 @@ This package is designed for n8n's community node ecosystem and uses the n8n nod
170
206
  - **Implementation language:** TypeScript.
171
207
  - **API:** Pinterest API v5.
172
208
  - **Authentication:** OAuth2.
173
- - **Package distribution:** npm.
174
- - **Development package manager:** pnpm.
209
+ - **Package distribution:** npm — published by GitHub Actions with npm provenance.
210
+ - **Development package manager:** pnpm — pinned through the `packageManager` field in `package.json` and `pnpm-lock.yaml`.
175
211
 
176
212
  The package declares its n8n node metadata and entry points in `package.json`. Compatibility depends on the n8n version, the available `n8n-workflow` APIs, and the package's dependencies.
177
213
 
178
214
  Refer to the repository's package metadata and release notes when evaluating compatibility with a particular n8n version.
179
215
 
180
- The initial `0.1.0` release is a pre-1.0 release. Operation coverage and compatibility may evolve as the integration develops.
216
+ The `0.1.x` releases are pre-1.0 releases. Operation coverage and compatibility may evolve as the integration develops.
217
+
218
+ ## 🛠️ Development
219
+
220
+ ### Requirements
221
+
222
+ - **[Node.js](https://nodejs.org/)** (v22 or higher) and **pnpm**
223
+ - **[git](https://git-scm.com/downloads)**
224
+ - **[Corepack](https://nodejs.org/api/corepack.html)** (optional) — pnpm 12.4.2 is pinned in `package.json` via the `packageManager` field
225
+
226
+ ```bash
227
+ corepack enable
228
+ pnpm install
229
+ ```
230
+
231
+ ### Available scripts
232
+
233
+ | Script | Description |
234
+ | --- | --- |
235
+ | `pnpm run dev` | Start n8n with the Pinterest node and watch for changes. |
236
+ | `pnpm run build` | Compile TypeScript to JavaScript for production. |
237
+ | `pnpm run build:watch` | Build in watch mode. |
238
+ | `pnpm run lint` | Check code for errors and style issues. |
239
+ | `pnpm run lint:fix` | Automatically fix lint issues where possible. |
240
+ | `pnpm run release:local` | Create a local release: bump the version, update the changelog, commit, tag (`vX.Y.Z`), and push. |
241
+ | `pnpm run release` | The GitHub Actions publish entry point; builds and publishes to npm with provenance. |
242
+
243
+ > **Note:** Use `pnpm run release:local` to create and push a release tag. The repository's `.release-it.json` configures the `master` branch, the `vX.Y.Z` tag format, and changelog generation.
244
+
245
+ ### Release process
246
+
247
+ Releases are published by GitHub Actions, never from a local machine, so every published version carries an npm provenance attestation (required by n8n for verified community nodes).
248
+
249
+ 1. Make sure `master` is up to date and the working tree is clean.
250
+ 2. Run `pnpm run release:local`. It lints, builds, prompts for a version bump, updates `CHANGELOG.md`, commits, creates a `vX.Y.Z` tag, and pushes it.
251
+ 3. The tag push triggers `.github/workflows/publish.yml`, which checks out the tag, verifies it matches `package.json`, checks whether the version is already on npm, and publishes it with provenance.
252
+
253
+ > **Note:** release-it also creates a GitHub release and needs a `GITHUB_TOKEN` with `repo` scope (for example, `export GITHUB_TOKEN=$(gh auth token)`). Without it, release-it opens the GitHub release page in a browser.
254
+
255
+ ### Required repository configuration
256
+
257
+ - A repository secret named `NPM_TOKEN` holding an npm granular access token with **read and write** publish permission for `@sinehypernova-0718/n8n-nodes-pinterest`.
258
+ - The default branch must be `master`; CI and the publish workflow are configured for it.
259
+ - `@n8n/node-cli` version 0.23.0 or later is required for npm provenance support.
181
260
 
182
261
  ## 🚀 Usage
183
262
 
@@ -188,12 +267,24 @@ After installing the node and configuring OAuth2 credentials, you can incorporat
188
267
  1. Create a workflow in n8n.
189
268
  2. Add the Pinterest node.
190
269
  3. Select your Pinterest OAuth2 credential.
191
- 4. Choose the resource you want to work with, such as **Pin** or **Board**.
270
+ 4. Choose the resource you want to work with, such as **Pin**, **Board**, or **Media**.
192
271
  5. Select the required operation.
193
272
  6. Configure its parameters, IDs, filters, and other required fields.
194
273
  7. Connect the node to the other nodes in your workflow.
195
274
  8. Execute the workflow and inspect the returned data.
196
275
 
276
+ ### Creating a video Pin
277
+
278
+ Video Pins require a video upload before the Pin itself is created.
279
+
280
+ 1. Add a node that provides the video as binary data, such as an HTTP Request or file storage node.
281
+ 2. Add the Pinterest node and choose **Media → Upload Video**.
282
+ 3. Set **Binary Property** to the property that contains the video, and configure **Wait for Processing** if needed.
283
+ 4. Execute the node. The output includes the `media_id`, `media_type`, and processing `status`.
284
+ 5. Add another Pinterest node and choose **Pin → Create**.
285
+ 6. Set **Media Source Type** to **Video ID**, then set **Media ID** to the uploaded `media_id`, for example `{{ $json.media_id }}`.
286
+ 7. Provide a **Cover Image URL** (Pinterest requires a valid cover image for video Pins), then execute the Pin creation.
287
+
197
288
  ### Troubleshooting
198
289
 
199
290
  If an operation fails:
@@ -217,6 +308,31 @@ For general workflow guidance, see the [n8n Try it out documentation](https://do
217
308
 
218
309
  ## 📝 Version history
219
310
 
311
+ ### `0.1.2`
312
+
313
+ Adds video media uploads and product-tag management to the Pinterest integration for n8n.
314
+
315
+ - Adds the **Media** resource with **Upload Video**, **Get Upload**, and **List Uploads** operations.
316
+ - Supports MP4, M4V, and MOV video uploads up to 512 MB, with optional processing polling and sanitized upload responses.
317
+ - Adds **Add Product Tags**, **List Product Tags**, and **Remove Product Tags** operations to the **Pin** resource.
318
+ - Validates hero and product Pin IDs locally, rejects empty product Pin IDs, and enforces the 1–24 limit and duplicate rejection for Add Product Tags.
319
+ - Documents product-tag eligibility rejection codes and association-only removal semantics.
320
+ - Adds bookmark pagination for media upload listings.
321
+ - Updates release automation, publish workflow handling, node metadata, and brand assets.
322
+ - Expands README documentation for media uploads, product tags, permissions, and release details.
323
+
324
+ This release extends the integration with video media handling and Pin product-tag management while preserving the existing resources and operations.
325
+
326
+ ### `0.1.1`
327
+
328
+ Adds board section management to the Pinterest integration for n8n.
329
+
330
+ - Adds Create Board Section, Delete Board Section, Update Board Section, and List Board Sections operations.
331
+ - Adds List Pins on Board Section to retrieve Pins associated with a board section.
332
+ - Includes pagination for the new board section listing operations where implemented.
333
+
334
+ This release extends the Board resource with board section management while preserving the existing integration structure.
335
+
220
336
  ### `0.1.0`
221
337
 
222
338
  Initial pre-1.0 release of the Pinterest integration for n8n.
@@ -0,0 +1,17 @@
1
+ <svg width="480" height="96" viewBox="0 0 480 96" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <g clip-path="url(#clip0_589_96)">
3
+ <path d="M48 96C52.5 96 54 94.5 54 90V78C54 60 60 54 78 54H90C108 54 114 60 114 78V90C114 94.5 115.5 96 120 96C124.5 96 126 94.5 126 90V78C126 60 132 54 150 54H162C180 54 186 60 186 78V90C186 94.5 187.5 96 192 96C196.5 96 198 94.5 198 90V78C198 60 204 54 222 54H234C238.5 54 240 52.5 240 48C240 43.5 238.5 42 234 42H222C204 42 198 36 198 18V6C198 1.5 196.5 0 192 0C187.5 0 186 1.5 186 6V18C186 36 180 42 162 42H150C132 42 126 36 126 18V6C126 1.5 124.5 0 120 0C115.5 0 114 1.5 114 6V18C114 36 108 42 90 42H78C60 42 54 36 54 18V6C54 1.5 52.5 0 48 0C43.5 0 42 1.5 42 6V18C42 36 36 42 18 42H6C1.5 42 0 43.5 0 48C0 52.5 1.5 54 6 54H18C36 54 42 60 42 78V90C42 94.5 43.5 96 48 96Z" fill="#191919"/>
4
+ </g>
5
+ <path d="M396 85.5C411.75 85.5 422.25 77.1562 422.25 64.5938C422.25 55.7812 417.562 49.3125 409.312 46.125C416.156 43.125 420 37.5 420 30.0938C420 18.375 410.438 10.5 396 10.5C381.562 10.5 372 18.375 372 30.0938C372 37.5 375.844 43.125 382.688 46.125C374.438 49.3125 369.75 55.7812 369.75 64.5938C369.75 77.1562 380.25 85.5 396 85.5ZM312 84H324V60.2812C324 46.6875 329.531 42.1875 337.688 42.1875C344.719 42.1875 348 45.4688 348 55.2188V84H360V51C360 39 353.625 31.5 340.969 31.5C332.625 31.5 327 34.9688 323.625 40.875V33H312V84ZM396 75C387.656 75 382.5 70.5938 382.5 63.2812C382.5 55.9688 387.656 51.5625 396 51.5625C404.344 51.5625 409.5 55.9688 409.5 63.2812C409.5 70.5938 404.344 75 396 75ZM396 41.625C388.781 41.625 384 37.5 384 31.3125C384 25.125 388.781 21 396 21C403.219 21 408 25.125 408 31.3125C408 37.5 403.219 41.625 396 41.625ZM432 84H444V60.2812C444 46.6875 449.531 42.1875 457.688 42.1875C464.719 42.1875 468 45.4688 468 55.2188V84H480V51C480 39 473.625 31.5 460.969 31.5C452.625 31.5 447 34.9688 443.625 40.875V33H432V84Z" fill="#191919"/>
6
+ <g clip-path="url(#clip1_589_96)">
7
+ <path d="M48 96C52.5 96 54 94.5 54 90V78C54 60 60 54 78 54H90C108 54 114 60 114 78V90C114 94.5 115.5 96 120 96C124.5 96 126 94.5 126 90V78C126 60 132 54 150 54H162C180 54 186 60 186 78V90C186 94.5 187.5 96 192 96C196.5 96 198 94.5 198 90V78C198 60 204 54 222 54H234C238.5 54 240 52.5 240 48C240 43.5 238.5 42 234 42H222C204 42 198 36 198 18V6C198 1.5 196.5 0 192 0C187.5 0 186 1.5 186 6V18C186 36 180 42 162 42H150C132 42 126 36 126 18V6C126 1.5 124.5 0 120 0C115.5 0 114 1.5 114 6V18C114 36 108 42 90 42H78C60 42 54 36 54 18V6C54 1.5 52.5 0 48 0C43.5 0 42 1.5 42 6V18C42 36 36 42 18 42H6C1.5 42 0 43.5 0 48C0 52.5 1.5 54 6 54H18C36 54 42 60 42 78V90C42 94.5 43.5 96 48 96Z" fill="#FF91AC"/>
8
+ </g>
9
+ <defs>
10
+ <clipPath id="clip0_589_96">
11
+ <rect width="240" height="96" fill="white"/>
12
+ </clipPath>
13
+ <clipPath id="clip1_589_96">
14
+ <rect width="240" height="96" fill="white"/>
15
+ </clipPath>
16
+ </defs>
17
+ </svg>
@@ -2,10 +2,10 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.Pinterest = void 0;
4
4
  const n8n_workflow_1 = require("n8n-workflow");
5
+ const constants_1 = require("./constants");
5
6
  const descriptions_1 = require("./descriptions");
6
7
  const errors_1 = require("./errors");
7
8
  const requests_1 = require("./requests");
8
- const PINTEREST_API_BASE_URL = 'https://api.pinterest.com/v5';
9
9
  async function collectPinListPages(firstPageRequest, itemIndex) {
10
10
  var _a;
11
11
  const returnAll = this.getNodeParameter('returnAll', itemIndex, false);
@@ -15,7 +15,7 @@ async function collectPinListPages(firstPageRequest, itemIndex) {
15
15
  const collected = [];
16
16
  const seenBookmarks = new Set();
17
17
  let requestOptions = {
18
- baseURL: PINTEREST_API_BASE_URL,
18
+ baseURL: constants_1.PINTEREST_API_BASE_URL,
19
19
  json: true,
20
20
  ...firstPageRequest,
21
21
  };
@@ -46,7 +46,52 @@ async function collectPinListPages(firstPageRequest, itemIndex) {
46
46
  seenBookmarks.add(nextBookmark);
47
47
  const nextPageRequest = await requests_1.listPinsRequest.call(this, itemIndex, nextBookmark);
48
48
  requestOptions = {
49
- baseURL: PINTEREST_API_BASE_URL,
49
+ baseURL: constants_1.PINTEREST_API_BASE_URL,
50
+ json: true,
51
+ ...nextPageRequest,
52
+ };
53
+ }
54
+ return returnAll ? collected : collected.slice(0, limit);
55
+ }
56
+ async function collectMediaListPages(firstPageRequest, itemIndex) {
57
+ var _a;
58
+ const returnAll = this.getNodeParameter('returnAll', itemIndex, false);
59
+ const limit = returnAll
60
+ ? Number.POSITIVE_INFINITY
61
+ : this.getNodeParameter('limit', itemIndex, 50);
62
+ const collected = [];
63
+ const seenBookmarks = new Set();
64
+ let requestOptions = {
65
+ baseURL: constants_1.PINTEREST_API_BASE_URL,
66
+ json: true,
67
+ ...firstPageRequest,
68
+ };
69
+ const initialBookmark = (_a = firstPageRequest.qs) === null || _a === void 0 ? void 0 : _a.bookmark;
70
+ if (typeof initialBookmark === 'string' && initialBookmark !== '') {
71
+ seenBookmarks.add(initialBookmark);
72
+ }
73
+ while (true) {
74
+ let response;
75
+ try {
76
+ response = (await this.helpers.httpRequestWithAuthentication.call(this, 'pinterestOAuth2Api', requestOptions));
77
+ }
78
+ catch (error) {
79
+ throw (0, errors_1.mapPinterestApiError)(error, { itemIndex });
80
+ }
81
+ collected.push(...(0, requests_1.normalizeMediaUploadList)(response, this.getNode(), itemIndex));
82
+ if (collected.length >= limit) {
83
+ break;
84
+ }
85
+ const nextBookmark = response === null || response === void 0 ? void 0 : response.bookmark;
86
+ if (typeof nextBookmark !== 'string' ||
87
+ nextBookmark === '' ||
88
+ seenBookmarks.has(nextBookmark)) {
89
+ break;
90
+ }
91
+ seenBookmarks.add(nextBookmark);
92
+ const nextPageRequest = await requests_1.listMediaRequest.call(this, itemIndex, nextBookmark);
93
+ requestOptions = {
94
+ baseURL: constants_1.PINTEREST_API_BASE_URL,
50
95
  json: true,
51
96
  ...nextPageRequest,
52
97
  };
@@ -58,7 +103,7 @@ async function collectBoardListPages(firstPageRequest, itemIndex) {
58
103
  const collected = [];
59
104
  const seenBookmarks = new Set();
60
105
  let requestOptions = {
61
- baseURL: PINTEREST_API_BASE_URL,
106
+ baseURL: constants_1.PINTEREST_API_BASE_URL,
62
107
  json: true,
63
108
  ...firstPageRequest,
64
109
  };
@@ -86,7 +131,7 @@ async function collectBoardListPages(firstPageRequest, itemIndex) {
86
131
  seenBookmarks.add(nextBookmark);
87
132
  const nextPageRequest = await requests_1.listBoardsRequest.call(this, itemIndex, nextBookmark);
88
133
  requestOptions = {
89
- baseURL: PINTEREST_API_BASE_URL,
134
+ baseURL: constants_1.PINTEREST_API_BASE_URL,
90
135
  json: true,
91
136
  ...nextPageRequest,
92
137
  };
@@ -98,7 +143,7 @@ async function collectBoardPinsListPages(firstPageRequest, itemIndex) {
98
143
  const collected = [];
99
144
  const seenBookmarks = new Set();
100
145
  let requestOptions = {
101
- baseURL: PINTEREST_API_BASE_URL,
146
+ baseURL: constants_1.PINTEREST_API_BASE_URL,
102
147
  json: true,
103
148
  ...firstPageRequest,
104
149
  };
@@ -126,7 +171,87 @@ async function collectBoardPinsListPages(firstPageRequest, itemIndex) {
126
171
  seenBookmarks.add(nextBookmark);
127
172
  const nextPageRequest = await requests_1.listBoardPinsRequest.call(this, itemIndex, nextBookmark);
128
173
  requestOptions = {
129
- baseURL: PINTEREST_API_BASE_URL,
174
+ baseURL: constants_1.PINTEREST_API_BASE_URL,
175
+ json: true,
176
+ ...nextPageRequest,
177
+ };
178
+ }
179
+ return collected;
180
+ }
181
+ async function collectBoardSectionPinsListPages(firstPageRequest, itemIndex) {
182
+ var _a;
183
+ const collected = [];
184
+ const seenBookmarks = new Set();
185
+ let requestOptions = {
186
+ baseURL: constants_1.PINTEREST_API_BASE_URL,
187
+ json: true,
188
+ ...firstPageRequest,
189
+ };
190
+ const initialBookmark = (_a = firstPageRequest.qs) === null || _a === void 0 ? void 0 : _a.bookmark;
191
+ if (typeof initialBookmark === 'string' && initialBookmark !== '') {
192
+ seenBookmarks.add(initialBookmark);
193
+ }
194
+ while (true) {
195
+ let response;
196
+ try {
197
+ response = (await this.helpers.httpRequestWithAuthentication.call(this, 'pinterestOAuth2Api', requestOptions));
198
+ }
199
+ catch (error) {
200
+ throw (0, errors_1.mapPinterestApiError)(error, { itemIndex });
201
+ }
202
+ if (Array.isArray(response === null || response === void 0 ? void 0 : response.items)) {
203
+ collected.push(...response.items);
204
+ }
205
+ const nextBookmark = response === null || response === void 0 ? void 0 : response.bookmark;
206
+ if (typeof nextBookmark !== 'string' ||
207
+ nextBookmark === '' ||
208
+ seenBookmarks.has(nextBookmark)) {
209
+ break;
210
+ }
211
+ seenBookmarks.add(nextBookmark);
212
+ const nextPageRequest = await requests_1.listBoardSectionPinsRequest.call(this, itemIndex, nextBookmark);
213
+ requestOptions = {
214
+ baseURL: constants_1.PINTEREST_API_BASE_URL,
215
+ json: true,
216
+ ...nextPageRequest,
217
+ };
218
+ }
219
+ return collected;
220
+ }
221
+ async function collectBoardSectionsListPages(firstPageRequest, itemIndex) {
222
+ var _a;
223
+ const collected = [];
224
+ const seenBookmarks = new Set();
225
+ let requestOptions = {
226
+ baseURL: constants_1.PINTEREST_API_BASE_URL,
227
+ json: true,
228
+ ...firstPageRequest,
229
+ };
230
+ const initialBookmark = (_a = firstPageRequest.qs) === null || _a === void 0 ? void 0 : _a.bookmark;
231
+ if (typeof initialBookmark === 'string' && initialBookmark !== '') {
232
+ seenBookmarks.add(initialBookmark);
233
+ }
234
+ while (true) {
235
+ let response;
236
+ try {
237
+ response = (await this.helpers.httpRequestWithAuthentication.call(this, 'pinterestOAuth2Api', requestOptions));
238
+ }
239
+ catch (error) {
240
+ throw (0, errors_1.mapPinterestApiError)(error, { itemIndex });
241
+ }
242
+ if (Array.isArray(response === null || response === void 0 ? void 0 : response.items)) {
243
+ collected.push(...response.items);
244
+ }
245
+ const nextBookmark = response === null || response === void 0 ? void 0 : response.bookmark;
246
+ if (typeof nextBookmark !== 'string' ||
247
+ nextBookmark === '' ||
248
+ seenBookmarks.has(nextBookmark)) {
249
+ break;
250
+ }
251
+ seenBookmarks.add(nextBookmark);
252
+ const nextPageRequest = await requests_1.listBoardSectionsRequest.call(this, itemIndex, nextBookmark);
253
+ requestOptions = {
254
+ baseURL: constants_1.PINTEREST_API_BASE_URL,
130
255
  json: true,
131
256
  ...nextPageRequest,
132
257
  };
@@ -170,11 +295,16 @@ class Pinterest {
170
295
  name: 'Board',
171
296
  value: 'board',
172
297
  },
298
+ {
299
+ name: 'Media',
300
+ value: 'media',
301
+ },
173
302
  ],
174
303
  default: 'pin',
175
304
  },
176
305
  ...descriptions_1.pinFields,
177
306
  ...descriptions_1.boardFields,
307
+ ...descriptions_1.mediaFields,
178
308
  ],
179
309
  };
180
310
  }
@@ -186,9 +316,13 @@ class Pinterest {
186
316
  const resource = this.getNodeParameter('resource', i);
187
317
  const operation = this.getNodeParameter('operation', i);
188
318
  let requestOptions;
319
+ let responseData;
189
320
  switch (resource) {
190
321
  case 'pin':
191
322
  switch (operation) {
323
+ case 'addProductTags':
324
+ requestOptions = await requests_1.addProductTagsRequest.call(this, i);
325
+ break;
192
326
  case 'create':
193
327
  requestOptions = await requests_1.createPinRequest.call(this, i);
194
328
  break;
@@ -198,6 +332,9 @@ class Pinterest {
198
332
  case 'list':
199
333
  requestOptions = await requests_1.listPinsRequest.call(this, i);
200
334
  break;
335
+ case 'listProductTags':
336
+ requestOptions = await requests_1.listProductTagsRequest.call(this, i);
337
+ break;
201
338
  case 'getMultiple':
202
339
  requestOptions = await requests_1.getMultiplePinsRequest.call(this, i);
203
340
  break;
@@ -210,6 +347,9 @@ class Pinterest {
210
347
  case 'delete':
211
348
  requestOptions = await requests_1.deletePinRequest.call(this, i);
212
349
  break;
350
+ case 'removeProductTags':
351
+ requestOptions = await requests_1.removeProductTagsRequest.call(this, i);
352
+ break;
213
353
  case 'save':
214
354
  requestOptions = await requests_1.savePinRequest.call(this, i);
215
355
  break;
@@ -222,6 +362,9 @@ class Pinterest {
222
362
  case 'create':
223
363
  requestOptions = await requests_1.createBoardRequest.call(this, i);
224
364
  break;
365
+ case 'createSection':
366
+ requestOptions = await requests_1.createBoardSectionRequest.call(this, i);
367
+ break;
225
368
  case 'get':
226
369
  requestOptions = await requests_1.getBoardRequest.call(this, i);
227
370
  break;
@@ -231,42 +374,88 @@ class Pinterest {
231
374
  case 'listPins':
232
375
  requestOptions = await requests_1.listBoardPinsRequest.call(this, i);
233
376
  break;
377
+ case 'listSections':
378
+ requestOptions = await requests_1.listBoardSectionsRequest.call(this, i);
379
+ break;
380
+ case 'listSectionPins':
381
+ requestOptions = await requests_1.listBoardSectionPinsRequest.call(this, i);
382
+ break;
234
383
  case 'delete':
235
384
  requestOptions = await requests_1.deleteBoardRequest.call(this, i);
236
385
  break;
386
+ case 'deleteSection':
387
+ requestOptions = await requests_1.deleteBoardSectionRequest.call(this, i);
388
+ break;
237
389
  case 'update':
238
390
  requestOptions = await requests_1.updateBoardRequest.call(this, i);
239
391
  break;
392
+ case 'updateSection':
393
+ requestOptions = await requests_1.updateBoardSectionRequest.call(this, i);
394
+ break;
240
395
  default:
241
396
  throw new n8n_workflow_1.NodeOperationError(this.getNode(), `The Board operation "${operation}" is not implemented yet`, { itemIndex: i });
242
397
  }
243
398
  break;
399
+ case 'media':
400
+ switch (operation) {
401
+ case 'upload':
402
+ responseData = await requests_1.uploadMediaVideo.call(this, i);
403
+ break;
404
+ case 'get':
405
+ requestOptions = await requests_1.getMediaRequest.call(this, i);
406
+ break;
407
+ case 'list':
408
+ requestOptions = await requests_1.listMediaRequest.call(this, i);
409
+ break;
410
+ default:
411
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), `The Media operation "${operation}" is not implemented yet`, { itemIndex: i });
412
+ }
413
+ break;
244
414
  default:
245
415
  throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Unsupported resource: ${resource}`, {
246
416
  itemIndex: i,
247
417
  });
248
418
  }
249
- let responseData;
250
- if (resource === 'pin' && operation === 'list') {
251
- responseData = await collectPinListPages.call(this, requestOptions, i);
252
- }
253
- else if (resource === 'board' && operation === 'list') {
254
- responseData = await collectBoardListPages.call(this, requestOptions, i);
255
- }
256
- else if (resource === 'board' && operation === 'listPins') {
257
- responseData = await collectBoardPinsListPages.call(this, requestOptions, i);
258
- }
259
- else {
260
- const options = {
261
- baseURL: PINTEREST_API_BASE_URL,
262
- json: true,
263
- ...requestOptions,
264
- };
265
- try {
266
- responseData = await this.helpers.httpRequestWithAuthentication.call(this, 'pinterestOAuth2Api', options);
419
+ if (responseData === undefined) {
420
+ if (requestOptions === undefined) {
421
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), `The ${resource} operation "${operation}" did not produce a request`, { itemIndex: i });
422
+ }
423
+ if (resource === 'pin' && operation === 'list') {
424
+ responseData = await collectPinListPages.call(this, requestOptions, i);
267
425
  }
268
- catch (error) {
269
- throw (0, errors_1.mapPinterestApiError)(error, { itemIndex: i });
426
+ else if (resource === 'board' && operation === 'list') {
427
+ responseData = await collectBoardListPages.call(this, requestOptions, i);
428
+ }
429
+ else if (resource === 'board' && operation === 'listPins') {
430
+ responseData = await collectBoardPinsListPages.call(this, requestOptions, i);
431
+ }
432
+ else if (resource === 'board' && operation === 'listSections') {
433
+ responseData = await collectBoardSectionsListPages.call(this, requestOptions, i);
434
+ }
435
+ else if (resource === 'board' && operation === 'listSectionPins') {
436
+ responseData = await collectBoardSectionPinsListPages.call(this, requestOptions, i);
437
+ }
438
+ else if (resource === 'media' && operation === 'list') {
439
+ responseData = await collectMediaListPages.call(this, requestOptions, i);
440
+ }
441
+ else {
442
+ const options = {
443
+ baseURL: constants_1.PINTEREST_API_BASE_URL,
444
+ json: true,
445
+ ...requestOptions,
446
+ };
447
+ try {
448
+ responseData = await this.helpers.httpRequestWithAuthentication.call(this, 'pinterestOAuth2Api', options);
449
+ }
450
+ catch (error) {
451
+ throw (0, errors_1.mapPinterestApiError)(error, { itemIndex: i });
452
+ }
453
+ if (resource === 'media' && operation === 'get') {
454
+ responseData = (0, requests_1.normalizeMediaUpload)(responseData, this.getNode(), i);
455
+ }
456
+ if (resource === 'pin' && operation === 'removeProductTags') {
457
+ responseData = (0, requests_1.normalizeProductTagRemovalResponse)(responseData, this.getNode(), i);
458
+ }
270
459
  }
271
460
  }
272
461
  const executionData = this.helpers.constructExecutionMetaData(this.helpers.returnJsonArray(responseData !== null && responseData !== void 0 ? responseData : {}), { itemData: { item: i } });
@@ -279,7 +468,7 @@ class Pinterest {
279
468
  continue;
280
469
  }
281
470
  if (error instanceof n8n_workflow_1.NodeApiError || error instanceof n8n_workflow_1.NodeOperationError) {
282
- throw error;
471
+ throw (0, errors_1.mapPinterestApiError)(error, { itemIndex: i });
283
472
  }
284
473
  throw new n8n_workflow_1.NodeOperationError(this.getNode(), error, { itemIndex: i });
285
474
  }