From a3735a80ddd87000341a4342f82b709955d2212b Mon Sep 17 00:00:00 2001 From: Alex Rock Date: Tue, 24 Sep 2024 01:25:14 -0600 Subject: [PATCH 1/3] chore: add /validators folder --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 85b5c4551..120618cd7 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,8 @@ "flatfilers/*", "plugins/*", "support/*", - "utils/*" + "utils/*", + "validators/*" ], "scripts": { "clean": "find ./ '(' -name 'node_modules' -o -name 'dist' -o -name '.turbo' -o -name '.parcel-cache' ')' -type d -exec rm -rf {} +", From 976e37732ed25d2cc09e42dce6ec4e8dfbfe35d5 Mon Sep 17 00:00:00 2001 From: "Alex Rock (Koala)" Date: Wed, 25 Sep 2024 10:07:22 -0600 Subject: [PATCH 2/3] koala: initial commit --- validators/WordpressCMSExporter/README.MD | 59 ++++++++++ validators/WordpressCMSExporter/metadata.json | 105 ++++++++++++++++++ validators/WordpressCMSExporter/package.json | 69 ++++++++++++ .../WordpressCMSExporter/rollup.config.mjs | 47 ++++++++ validators/WordpressCMSExporter/src/index.ts | 93 ++++++++++++++++ 5 files changed, 373 insertions(+) create mode 100644 validators/WordpressCMSExporter/README.MD create mode 100644 validators/WordpressCMSExporter/metadata.json create mode 100644 validators/WordpressCMSExporter/package.json create mode 100644 validators/WordpressCMSExporter/rollup.config.mjs create mode 100644 validators/WordpressCMSExporter/src/index.ts diff --git a/validators/WordpressCMSExporter/README.MD b/validators/WordpressCMSExporter/README.MD new file mode 100644 index 000000000..5502c4836 --- /dev/null +++ b/validators/WordpressCMSExporter/README.MD @@ -0,0 +1,59 @@ +# Flatfile to WordPress Exporter + +This plugin integrates Flatfile with WordPress, allowing for seamless export of records from Flatfile to WordPress posts. It uses the Flatfile Listener and Record Hook plugin to process records and map them to WordPress post fields. + +## Features + +- Automatic field mapping from Flatfile to WordPress post fields +- Support for custom fields +- Handling of categories and tags as arrays +- Ability to update existing posts or create new ones +- Error handling and reporting + +## Installation + +To install this plugin, run the following command: + +```bash +npm install @flatfile/listener @flatfile/plugin-record-hook @flatfile/api axios +``` + +## Example Usage + +```javascript +import { FlatfileListener } from '@flatfile/listener'; +import { recordHook } from '@flatfile/plugin-record-hook'; +import api from '@flatfile/api'; +import axios from 'axios'; + +const listener = FlatfileListener.create((listener) => { + listener.use( + recordHook('contacts', async (record, event) => { + // ... (code implementation as provided in the original snippet) + }) + ); +}); + +export default listener; +``` + +## Configuration + +To use this plugin, you need to set up the following in your Flatfile space metadata: + +- `wpApiUrl`: The URL of your WordPress API +- `wpApiKey`: Your WordPress API authentication key + +These credentials are used to authenticate requests to the WordPress API. + +## Behavior + +1. The plugin processes each record in the 'contacts' sheet. +2. It maps Flatfile fields to WordPress post fields according to a predefined mapping. +3. Special handling is applied for categories, tags, and custom fields. +4. The plugin checks if a post with the same title already exists in WordPress. +5. If an existing post is found, it updates the post; otherwise, it creates a new post. +6. The WordPress post ID is stored back in the Flatfile record. +7. Any errors during the export process are logged and added to the record. + +Note: This plugin assumes that the 'title' field is used as a unique identifier for posts. Adjust this logic if a different field should be used for identifying existing posts. \ No newline at end of file diff --git a/validators/WordpressCMSExporter/metadata.json b/validators/WordpressCMSExporter/metadata.json new file mode 100644 index 000000000..d5d8a17cf --- /dev/null +++ b/validators/WordpressCMSExporter/metadata.json @@ -0,0 +1,105 @@ +{ + "timestamp": "2024-09-25T06-27-21-653Z", + "task": "Create a Wordpress CMS Export Flatfile Listener plugin:\n - Implement a custom action to export data to Wordpress\n - Allow users to select the target CMS platform and provide necessary API credentials\n - Map Flatfile fields to corresponding CMS fields (e.g., title, content, author, categories, tags)\n - Support exporting rich text content, including formatting and embedded media\n - Implement options for post status (draft, published) and scheduling\n - Handle batch processing for efficient export of multiple posts\n - Provide error handling and reporting for failed exports\n - Allow configuration of default post settings (e.g., featured image, excerpt)\n - Implement a preview function to review post formatting before export\n - Support updating existing posts by matching on a unique identifier\n - Include options for handling custom fields specific to each CMS platform", + "summary": "This code implements a Flatfile Listener that uses the Record Hook plugin to process records and export them to WordPress. It includes functionality for field mapping, handling custom fields, and updating existing posts.", + "steps": [ + [ + "Retrieve general knowledge about Flatfile Listeners and the Record Hook plugin to understand the structure and capabilities we can utilize.\n", + "#E1", + "PineconeAssistant", + "Provide information on Flatfile Listeners and the Record Hook plugin, including their structure and capabilities", + "Plan: Retrieve general knowledge about Flatfile Listeners and the Record Hook plugin to understand the structure and capabilities we can utilize.\n#E1 = PineconeAssistant[Provide information on Flatfile Listeners and the Record Hook plugin, including their structure and capabilities]" + ], + [ + "Implement the custom action for exporting data to Wordpress, including user selection for the target CMS platform and API credential input.\n", + "#E3", + "LLM", + "Extend the code from #E2 to include a custom action for exporting data to Wordpress, allowing users to select the target CMS platform and provide API credentials", + "Plan: Implement the custom action for exporting data to Wordpress, including user selection for the target CMS platform and API credential input.\n#E3 = LLM[Extend the code from #E2 to include a custom action for exporting data to Wordpress, allowing users to select the target CMS platform and provide API credentials]" + ], + [ + "Implement field mapping functionality to map Flatfile fields to corresponding CMS fields.\n", + "#E4", + "LLM", + "Add field mapping functionality to the code from #E3, mapping Flatfile fields to CMS fields such as title, content, author, categories, and tags", + "Plan: Implement field mapping functionality to map Flatfile fields to corresponding CMS fields.\n#E4 = LLM[Add field mapping functionality to the code from #E3, mapping Flatfile fields to CMS fields such as title, content, author, categories, and tags]" + ], + [ + "Add support for exporting rich text content, including formatting and embedded media.\n", + "#E5", + "LLM", + "Extend the code from #E4 to support exporting rich text content, including formatting and embedded media", + "Plan: Add support for exporting rich text content, including formatting and embedded media.\n#E5 = LLM[Extend the code from #E4 to support exporting rich text content, including formatting and embedded media]" + ], + [ + "Implement options for post status (draft, published) and scheduling.\n", + "#E6", + "LLM", + "Add functionality to the code from #E5 for setting post status (draft, published) and scheduling", + "Plan: Implement options for post status (draft, published) and scheduling.\n#E6 = LLM[Add functionality to the code from #E5 for setting post status (draft, published) and scheduling]" + ], + [ + "Implement batch processing for efficient export of multiple posts.\n", + "#E7", + "LLM", + "Extend the code from #E6 to include batch processing functionality for efficient export of multiple posts", + "Plan: Implement batch processing for efficient export of multiple posts.\n#E7 = LLM[Extend the code from #E6 to include batch processing functionality for efficient export of multiple posts]" + ], + [ + "Add error handling and reporting for failed exports.\n", + "#E8", + "LLM", + "Implement error handling and reporting for failed exports in the code from #E7", + "Plan: Add error handling and reporting for failed exports.\n#E8 = LLM[Implement error handling and reporting for failed exports in the code from #E7]" + ], + [ + "Implement configuration options for default post settings.\n", + "#E9", + "LLM", + "Add configuration options for default post settings (e.g., featured image, excerpt) to the code from #E8", + "Plan: Implement configuration options for default post settings.\n#E9 = LLM[Add configuration options for default post settings (e.g., featured image, excerpt) to the code from #E8]" + ], + [ + "Implement a preview function to review post formatting before export.\n", + "#E10", + "LLM", + "Add a preview function to the code from #E9 to allow users to review post formatting before export", + "Plan: Implement a preview function to review post formatting before export.\n#E10 = LLM[Add a preview function to the code from #E9 to allow users to review post formatting before export]" + ], + [ + "Implement functionality to update existing posts by matching on a unique identifier.\n", + "#E11", + "LLM", + "Extend the code from #E10 to support updating existing posts by matching on a unique identifier", + "Plan: Implement functionality to update existing posts by matching on a unique identifier.\n#E11 = LLM[Extend the code from #E10 to support updating existing posts by matching on a unique identifier]" + ], + [ + "Add support for handling custom fields specific to each CMS platform.\n", + "#E12", + "LLM", + "Implement support for handling custom fields specific to each CMS platform in the code from #E11", + "Plan: Add support for handling custom fields specific to each CMS platform.\n#E12 = LLM[Implement support for handling custom fields specific to each CMS platform in the code from #E11]" + ], + [ + "Verify that the implemented Listener subscribes to valid Event Topics.\n", + "#E13", + "PineconeAssistant", + "Verify that the Event Topics used in the Listener implementation are valid according to the event.topics.fact file", + "Plan: Verify that the implemented Listener subscribes to valid Event Topics.\n#E13 = PineconeAssistant[Verify that the Event Topics used in the Listener implementation are valid according to the event.topics.fact file]" + ], + [ + "Review the final code, remove any unused imports, and validate the parameters for any plugins used.\n", + "#E14", + "LLM", + "Review the complete code from #E12, remove any unused imports, and validate the parameters for any plugins used. Ensure that the code is valid and follows best practices for Flatfile Listeners", + "Plan: Review the final code, remove any unused imports, and validate the parameters for any plugins used.\n#E14 = LLM[Review the complete code from #E12, remove any unused imports, and validate the parameters for any plugins used. Ensure that the code is valid and follows best practices for Flatfile Listeners]" + ] + ], + "metrics": { + "tokens": { + "plan": 5552, + "state": 7901, + "total": 13453 + } + } +} \ No newline at end of file diff --git a/validators/WordpressCMSExporter/package.json b/validators/WordpressCMSExporter/package.json new file mode 100644 index 000000000..fbbb8d786 --- /dev/null +++ b/validators/WordpressCMSExporter/package.json @@ -0,0 +1,69 @@ +{ + "name": "@flatfile/plugin-wordpress-exporter", + "version": "1.0.0", + "description": "A Flatfile plugin for exporting records to WordPress", + "main": "./dist/index.js", + "module": "./dist/index.mjs", + "types": "./dist/index.d.ts", + "browser": { + "./dist/index.js": "./dist/index.browser.js", + "./dist/index.mjs": "./dist/index.browser.mjs" + }, + "exports": { + "types": "./dist/index.d.ts", + "node": { + "import": "./dist/index.mjs", + "require": "./dist/index.js" + }, + "browser": { + "require": "./dist/index.browser.js", + "import": "./dist/index.browser.mjs" + }, + "default": "./dist/index.mjs" + }, + "source": "./src/index.ts", + "files": [ + "dist/**" + ], + "scripts": { + "build": "rollup -c", + "build:watch": "rollup -c --watch", + "build:prod": "NODE_ENV=production rollup -c", + "check": "tsc ./**/*.ts --noEmit --esModuleInterop", + "test": "jest ./**/*.spec.ts --config=../../jest.config.js --runInBand" + }, + "keywords": [ + "flatfile", + "plugin", + "wordpress", + "exporter", + "flatfile-plugins", + "category-transform" + ], + "author": "Your Name", + "license": "MIT", + "dependencies": { + "@flatfile/listener": "^1.0.5", + "@flatfile/plugin-record-hook": "^1.7.0", + "@flatfile/api": "^1.9.15", + "axios": "^1.7.7" + }, + "devDependencies": { + "@flatfile/hooks": "^1.5.0", + "@flatfile/rollup-config": "^0.1.1", + "typescript": "^5.6.2", + "@types/node": "^22.7.0", + "rollup": "^4.22.4", + "jest": "^29.7.0", + "@types/jest": "^29.5.13" + }, + "repository": { + "type": "git", + "url": "https://github.com/YourUsername/flatfile-plugin-wordpress-exporter.git" + }, + "browserslist": [ + "> 0.5%", + "last 2 versions", + "not dead" + ] +} \ No newline at end of file diff --git a/validators/WordpressCMSExporter/rollup.config.mjs b/validators/WordpressCMSExporter/rollup.config.mjs new file mode 100644 index 000000000..ed56cba03 --- /dev/null +++ b/validators/WordpressCMSExporter/rollup.config.mjs @@ -0,0 +1,47 @@ +import { buildConfig } from '@flatfile/rollup-config'; +import typescript from '@rollup/plugin-typescript'; +import resolve from '@rollup/plugin-node-resolve'; +import commonjs from '@rollup/plugin-commonjs'; +import json from '@rollup/plugin-json'; + +const umdExternals = [ + '@flatfile/api', + '@flatfile/hooks', + '@flatfile/listener', + '@flatfile/util-common', + '@flatfile/plugin-record-hook', + 'axios' +]; + +const config = buildConfig({ + input: 'src/index.ts', // Assuming your main file is src/index.ts + includeUmd: true, + umdConfig: { + name: 'FlatfileWordPressExport', + external: umdExternals + }, + plugins: [ + typescript({ + tsconfig: './tsconfig.json', + declaration: true, + declarationDir: 'dist/types' + }), + resolve({ + browser: true + }), + commonjs(), + json() + ] +}); + +// Add custom configuration for TypeScript handling +config.forEach(conf => { + if (conf.plugins) { + conf.plugins.unshift(typescript({ + tsconfig: './tsconfig.json', + declaration: false + })); + } +}); + +export default config; \ No newline at end of file diff --git a/validators/WordpressCMSExporter/src/index.ts b/validators/WordpressCMSExporter/src/index.ts new file mode 100644 index 000000000..0d6359a96 --- /dev/null +++ b/validators/WordpressCMSExporter/src/index.ts @@ -0,0 +1,93 @@ +import { FlatfileListener } from '@flatfile/listener' +import { recordHook } from '@flatfile/plugin-record-hook' +import api from '@flatfile/api' +import axios from 'axios' + +const listener = FlatfileListener.create((listener) => { + listener.use( + recordHook('contacts', async (record, event) => { + const { jobId, environmentId, spaceId, sheetId } = event.context + + // Field mapping + const fieldMapping = { + title: 'name', + content: 'description', + author: 'email', + categories: 'category', + tags: 'tags', + status: 'status', + custom_fields: 'custom_fields', + } + + // Prepare post data + const postData = {} + for (const [wpField, ffField] of Object.entries(fieldMapping)) { + if (record.get(ffField)) { + if (wpField === 'categories' || wpField === 'tags') { + postData[wpField] = record + .get(ffField) + .split(',') + .map((item) => item.trim()) + } else if (wpField === 'custom_fields') { + postData[wpField] = JSON.parse(record.get(ffField) || '{}') + } else { + postData[wpField] = record.get(ffField) + } + } + } + + try { + // Get WordPress API credentials from Flatfile space metadata + const { data: space } = await api.spaces.get(spaceId) + const wpApiUrl = space.metadata.wpApiUrl + const wpApiKey = space.metadata.wpApiKey + + if (!wpApiUrl || !wpApiKey) { + throw new Error( + 'WordPress API credentials not found in space metadata' + ) + } + + const headers = { + Authorization: `Bearer ${wpApiKey}`, + 'Content-Type': 'application/json', + } + + // Check if post already exists (assuming 'name' is unique identifier) + const existingPosts = await axios.get( + `${wpApiUrl}/wp-json/wp/v2/posts?search=${postData.title}`, + { headers } + ) + + let response + if (existingPosts.data.length > 0) { + // Update existing post + const postId = existingPosts.data[0].id + response = await axios.put( + `${wpApiUrl}/wp-json/wp/v2/posts/${postId}`, + postData, + { headers } + ) + } else { + // Create new post + response = await axios.post( + `${wpApiUrl}/wp-json/wp/v2/posts`, + postData, + { headers } + ) + } + + // Update record with WordPress post ID + record.set('wp_post_id', response.data.id) + + return record + } catch (error) { + console.error('Error exporting to WordPress:', error.message) + record.addError('wordpress_export', 'Failed to export to WordPress') + return record + } + }) + ) +}) + +export default listener From 3a9394daba1effb48fa2a0b93e97ccbabb1ad8de Mon Sep 17 00:00:00 2001 From: "Alex Rock (Koala)" Date: Wed, 25 Sep 2024 10:48:29 -0600 Subject: [PATCH 3/3] koala: initial commit --- validators/WordpressCMSExporter/README.MD | 78 +++--- validators/WordpressCMSExporter/metadata.json | 88 +++---- validators/WordpressCMSExporter/package.json | 19 +- .../WordpressCMSExporter/rollup.config.mjs | 27 +-- validators/WordpressCMSExporter/src/index.ts | 222 ++++++++++++------ 5 files changed, 246 insertions(+), 188 deletions(-) diff --git a/validators/WordpressCMSExporter/README.MD b/validators/WordpressCMSExporter/README.MD index 5502c4836..5086f1666 100644 --- a/validators/WordpressCMSExporter/README.MD +++ b/validators/WordpressCMSExporter/README.MD @@ -1,59 +1,71 @@ -# Flatfile to WordPress Exporter +# Flatfile WordPress CMS Export Plugin -This plugin integrates Flatfile with WordPress, allowing for seamless export of records from Flatfile to WordPress posts. It uses the Flatfile Listener and Record Hook plugin to process records and map them to WordPress post fields. +This Flatfile Listener plugin enables seamless export of data to WordPress CMS. It provides a robust solution for mapping fields, handling custom fields, applying default settings, previewing posts, and batch exporting to WordPress. ## Features -- Automatic field mapping from Flatfile to WordPress post fields -- Support for custom fields -- Handling of categories and tags as arrays -- Ability to update existing posts or create new ones +- Field mapping from Flatfile to WordPress post structure +- Custom field handling +- Default settings application +- Post preview generation +- Batch export to WordPress +- WordPress API authentication - Error handling and reporting ## Installation -To install this plugin, run the following command: +To install the plugin, use npm: ```bash -npm install @flatfile/listener @flatfile/plugin-record-hook @flatfile/api axios +npm install @flatfile/plugin-wordpress-export ``` ## Example Usage ```javascript -import { FlatfileListener } from '@flatfile/listener'; -import { recordHook } from '@flatfile/plugin-record-hook'; -import api from '@flatfile/api'; -import axios from 'axios'; - -const listener = FlatfileListener.create((listener) => { - listener.use( - recordHook('contacts', async (record, event) => { - // ... (code implementation as provided in the original snippet) - }) - ); -}); +import { FlatfileListener } from "@flatfile/listener"; +import WordpressCMSExport from "@flatfile/plugin-wordpress-export"; + +const listener = new FlatfileListener(); +const wordpressExport = WordpressCMSExport(listener); + +// Your additional listener configurations... export default listener; ``` ## Configuration -To use this plugin, you need to set up the following in your Flatfile space metadata: - -- `wpApiUrl`: The URL of your WordPress API -- `wpApiKey`: Your WordPress API authentication key +The plugin requires WordPress API credentials to be configured. You can set these in your Flatfile space metadata: -These credentials are used to authenticate requests to the WordPress API. +```javascript +await api.spaces.update(workspaceId, { + metadata: { + wpConfig: { + apiUrl: "https://your-wordpress-site.com", + username: "your-username", + password: "your-password", + }, + }, +}); +``` ## Behavior -1. The plugin processes each record in the 'contacts' sheet. -2. It maps Flatfile fields to WordPress post fields according to a predefined mapping. -3. Special handling is applied for categories, tags, and custom fields. -4. The plugin checks if a post with the same title already exists in WordPress. -5. If an existing post is found, it updates the post; otherwise, it creates a new post. -6. The WordPress post ID is stored back in the Flatfile record. -7. Any errors during the export process are logged and added to the record. +1. **Field Mapping**: The plugin maps Flatfile fields to WordPress post fields, including title, content, status, date, author, categories, and tags. + +2. **Custom Fields**: Any field prefixed with "custom_" in Flatfile will be treated as a custom field in WordPress. + +3. **Default Settings**: Default settings for post status, author, categories, and tags are applied if not provided in the Flatfile record. + +4. **Post Preview**: A HTML preview of each post is generated before export. + +5. **Batch Export**: Posts are exported in batches to the configured WordPress site using the WordPress REST API. + +6. **Error Handling**: Any errors during the export process are logged and reported back to Flatfile. + +7. **Job Progress**: The plugin updates the job progress in Flatfile, providing real-time feedback on the export process. + +8. **Workspace Metadata**: After a successful commit, the plugin updates the workspace metadata to indicate that WordPress configuration is required. -Note: This plugin assumes that the 'title' field is used as a unique identifier for posts. Adjust this logic if a different field should be used for identifying existing posts. \ No newline at end of file +This plugin streamlines the process of exporting data from Flatfile to WordPress, handling the complexities of field mapping, custom fields, and API interactions. \ No newline at end of file diff --git a/validators/WordpressCMSExporter/metadata.json b/validators/WordpressCMSExporter/metadata.json index d5d8a17cf..c8ff59578 100644 --- a/validators/WordpressCMSExporter/metadata.json +++ b/validators/WordpressCMSExporter/metadata.json @@ -1,105 +1,91 @@ { - "timestamp": "2024-09-25T06-27-21-653Z", + "timestamp": "2024-09-25T16-44-49-645Z", "task": "Create a Wordpress CMS Export Flatfile Listener plugin:\n - Implement a custom action to export data to Wordpress\n - Allow users to select the target CMS platform and provide necessary API credentials\n - Map Flatfile fields to corresponding CMS fields (e.g., title, content, author, categories, tags)\n - Support exporting rich text content, including formatting and embedded media\n - Implement options for post status (draft, published) and scheduling\n - Handle batch processing for efficient export of multiple posts\n - Provide error handling and reporting for failed exports\n - Allow configuration of default post settings (e.g., featured image, excerpt)\n - Implement a preview function to review post formatting before export\n - Support updating existing posts by matching on a unique identifier\n - Include options for handling custom fields specific to each CMS platform", - "summary": "This code implements a Flatfile Listener that uses the Record Hook plugin to process records and export them to WordPress. It includes functionality for field mapping, handling custom fields, and updating existing posts.", + "summary": "This code implements a Flatfile Listener plugin for exporting data to WordPress CMS. It includes functionality for mapping fields, handling custom fields, applying default settings, previewing posts, and batch exporting to WordPress. The plugin allows users to configure WordPress API credentials and select export options.", "steps": [ [ - "Retrieve general knowledge about Flatfile Listeners and the Record Hook plugin to understand the structure and capabilities we can utilize.\n", + "First, let's retrieve information about Flatfile Listeners and the Record Hook plugin to understand the basic structure we'll be working with.\n", "#E1", "PineconeAssistant", - "Provide information on Flatfile Listeners and the Record Hook plugin, including their structure and capabilities", - "Plan: Retrieve general knowledge about Flatfile Listeners and the Record Hook plugin to understand the structure and capabilities we can utilize.\n#E1 = PineconeAssistant[Provide information on Flatfile Listeners and the Record Hook plugin, including their structure and capabilities]" + "Provide information on Flatfile Listeners and the Record Hook plugin, including their basic structure and usage", + "Plan: First, let's retrieve information about Flatfile Listeners and the Record Hook plugin to understand the basic structure we'll be working with.\n#E1 = PineconeAssistant[Provide information on Flatfile Listeners and the Record Hook plugin, including their basic structure and usage]" ], [ - "Implement the custom action for exporting data to Wordpress, including user selection for the target CMS platform and API credential input.\n", + "Implement the custom action to export data to Wordpress. We'll need to use the Wordpress REST API for this.\n", "#E3", - "LLM", - "Extend the code from #E2 to include a custom action for exporting data to Wordpress, allowing users to select the target CMS platform and provide API credentials", - "Plan: Implement the custom action for exporting data to Wordpress, including user selection for the target CMS platform and API credential input.\n#E3 = LLM[Extend the code from #E2 to include a custom action for exporting data to Wordpress, allowing users to select the target CMS platform and provide API credentials]" + "Google", + "Wordpress REST API documentation for creating posts", + "Plan: Implement the custom action to export data to Wordpress. We'll need to use the Wordpress REST API for this.\n#E3 = Google[Wordpress REST API documentation for creating posts]" ], [ "Implement field mapping functionality to map Flatfile fields to corresponding CMS fields.\n", - "#E4", - "LLM", - "Add field mapping functionality to the code from #E3, mapping Flatfile fields to CMS fields such as title, content, author, categories, and tags", - "Plan: Implement field mapping functionality to map Flatfile fields to corresponding CMS fields.\n#E4 = LLM[Add field mapping functionality to the code from #E3, mapping Flatfile fields to CMS fields such as title, content, author, categories, and tags]" - ], - [ - "Add support for exporting rich text content, including formatting and embedded media.\n", "#E5", "LLM", - "Extend the code from #E4 to support exporting rich text content, including formatting and embedded media", - "Plan: Add support for exporting rich text content, including formatting and embedded media.\n#E5 = LLM[Extend the code from #E4 to support exporting rich text content, including formatting and embedded media]" + "Create a function called mapFields that takes in Flatfile record data and returns an object with mapped Wordpress fields (title, content, author, categories, tags). Include support for rich text content", + "Plan: Implement field mapping functionality to map Flatfile fields to corresponding CMS fields.\n#E5 = LLM[Create a function called mapFields that takes in Flatfile record data and returns an object with mapped Wordpress fields (title, content, author, categories, tags). Include support for rich text content]" ], [ - "Implement options for post status (draft, published) and scheduling.\n", + "Add support for post status and scheduling options.\n", "#E6", "LLM", - "Add functionality to the code from #E5 for setting post status (draft, published) and scheduling", - "Plan: Implement options for post status (draft, published) and scheduling.\n#E6 = LLM[Add functionality to the code from #E5 for setting post status (draft, published) and scheduling]" + "Extend the exportToWordpress function from #E4 to include options for post status (draft, published) and scheduling. Add these options to the function parameters", + "Plan: Add support for post status and scheduling options.\n#E6 = LLM[Extend the exportToWordpress function from #E4 to include options for post status (draft, published) and scheduling. Add these options to the function parameters]" ], [ "Implement batch processing for efficient export of multiple posts.\n", "#E7", "LLM", - "Extend the code from #E6 to include batch processing functionality for efficient export of multiple posts", - "Plan: Implement batch processing for efficient export of multiple posts.\n#E7 = LLM[Extend the code from #E6 to include batch processing functionality for efficient export of multiple posts]" + "Create a function called batchExport that takes an array of records and processes them in batches using the exportToWordpress function from #E6. Include error handling and reporting for failed exports", + "Plan: Implement batch processing for efficient export of multiple posts.\n#E7 = LLM[Create a function called batchExport that takes an array of records and processes them in batches using the exportToWordpress function from #E6. Include error handling and reporting for failed exports]" ], [ - "Add error handling and reporting for failed exports.\n", + "Add configuration options for default post settings.\n", "#E8", "LLM", - "Implement error handling and reporting for failed exports in the code from #E7", - "Plan: Add error handling and reporting for failed exports.\n#E8 = LLM[Implement error handling and reporting for failed exports in the code from #E7]" + "Create a function called applyDefaultSettings that takes in post data and default settings (e.g., featured image, excerpt) and applies them to the post data before export", + "Plan: Add configuration options for default post settings.\n#E8 = LLM[Create a function called applyDefaultSettings that takes in post data and default settings (e.g., featured image, excerpt) and applies them to the post data before export]" ], [ - "Implement configuration options for default post settings.\n", + "Implement a preview function to review post formatting before export.\n", "#E9", "LLM", - "Add configuration options for default post settings (e.g., featured image, excerpt) to the code from #E8", - "Plan: Implement configuration options for default post settings.\n#E9 = LLM[Add configuration options for default post settings (e.g., featured image, excerpt) to the code from #E8]" + "Create a function called previewPost that takes in mapped post data and returns a formatted preview of the post, including HTML rendering of rich text content", + "Plan: Implement a preview function to review post formatting before export.\n#E9 = LLM[Create a function called previewPost that takes in mapped post data and returns a formatted preview of the post, including HTML rendering of rich text content]" ], [ - "Implement a preview function to review post formatting before export.\n", + "Add support for updating existing posts by matching on a unique identifier.\n", "#E10", "LLM", - "Add a preview function to the code from #E9 to allow users to review post formatting before export", - "Plan: Implement a preview function to review post formatting before export.\n#E10 = LLM[Add a preview function to the code from #E9 to allow users to review post formatting before export]" + "Extend the exportToWordpress function from #E6 to check for existing posts using a unique identifier (e.g., slug or custom field) and update the post if it exists, otherwise create a new post", + "Plan: Add support for updating existing posts by matching on a unique identifier.\n#E10 = LLM[Extend the exportToWordpress function from #E6 to check for existing posts using a unique identifier (e.g., slug or custom field) and update the post if it exists, otherwise create a new post]" ], [ - "Implement functionality to update existing posts by matching on a unique identifier.\n", + "Implement options for handling custom fields specific to Wordpress.\n", "#E11", "LLM", - "Extend the code from #E10 to support updating existing posts by matching on a unique identifier", - "Plan: Implement functionality to update existing posts by matching on a unique identifier.\n#E11 = LLM[Extend the code from #E10 to support updating existing posts by matching on a unique identifier]" + "Create a function called handleCustomFields that takes in custom field data and adds it to the post data before export. Include support for common Wordpress custom field plugins", + "Plan: Implement options for handling custom fields specific to Wordpress.\n#E11 = LLM[Create a function called handleCustomFields that takes in custom field data and adds it to the post data before export. Include support for common Wordpress custom field plugins]" ], [ - "Add support for handling custom fields specific to each CMS platform.\n", + "Combine all the created functions into the main listener logic.\n", "#E12", "LLM", - "Implement support for handling custom fields specific to each CMS platform in the code from #E11", - "Plan: Add support for handling custom fields specific to each CMS platform.\n#E12 = LLM[Implement support for handling custom fields specific to each CMS platform in the code from #E11]" + "Combine the functions created in #E2, #E4, #E5, #E6, #E7, #E8, #E9, #E10, and #E11 into a complete Flatfile Listener plugin. Include logic to allow users to select export options and provide API credentials. Ensure that the listener subscribes only to valid Event Topics", + "Plan: Combine all the created functions into the main listener logic.\n#E12 = LLM[Combine the functions created in #E2, #E4, #E5, #E6, #E7, #E8, #E9, #E10, and #E11 into a complete Flatfile Listener plugin. Include logic to allow users to select export options and provide API credentials. Ensure that the listener subscribes only to valid Event Topics]" ], [ - "Verify that the implemented Listener subscribes to valid Event Topics.\n", + "Perform a final check on the complete plugin code, removing any unused imports and validating that all parameters are correct for the plugins used.\n", "#E13", - "PineconeAssistant", - "Verify that the Event Topics used in the Listener implementation are valid according to the event.topics.fact file", - "Plan: Verify that the implemented Listener subscribes to valid Event Topics.\n#E13 = PineconeAssistant[Verify that the Event Topics used in the Listener implementation are valid according to the event.topics.fact file]" - ], - [ - "Review the final code, remove any unused imports, and validate the parameters for any plugins used.\n", - "#E14", "LLM", - "Review the complete code from #E12, remove any unused imports, and validate the parameters for any plugins used. Ensure that the code is valid and follows best practices for Flatfile Listeners", - "Plan: Review the final code, remove any unused imports, and validate the parameters for any plugins used.\n#E14 = LLM[Review the complete code from #E12, remove any unused imports, and validate the parameters for any plugins used. Ensure that the code is valid and follows best practices for Flatfile Listeners]" + "Review the complete plugin code from #E12, remove any unused imports, and validate that all parameters are correct for the plugins used. Ensure that the listener only subscribes to valid Event Topics. Provide the final, cleaned-up version of the WordpressCMSExport plugin code", + "Plan: Perform a final check on the complete plugin code, removing any unused imports and validating that all parameters are correct for the plugins used.\n#E13 = LLM[Review the complete plugin code from #E12, remove any unused imports, and validate that all parameters are correct for the plugins used. Ensure that the listener only subscribes to valid Event Topics. Provide the final, cleaned-up version of the WordpressCMSExport plugin code]" ] ], "metrics": { "tokens": { - "plan": 5552, - "state": 7901, - "total": 13453 + "plan": 4324, + "state": 6167, + "total": 10491 } } } \ No newline at end of file diff --git a/validators/WordpressCMSExporter/package.json b/validators/WordpressCMSExporter/package.json index fbbb8d786..426e700f6 100644 --- a/validators/WordpressCMSExporter/package.json +++ b/validators/WordpressCMSExporter/package.json @@ -1,7 +1,7 @@ { - "name": "@flatfile/plugin-wordpress-exporter", + "name": "@flatfile/plugin-wordpress-cms-export", "version": "1.0.0", - "description": "A Flatfile plugin for exporting records to WordPress", + "description": "A Flatfile plugin for exporting data to WordPress CMS", "main": "./dist/index.js", "module": "./dist/index.mjs", "types": "./dist/index.d.ts", @@ -36,7 +36,8 @@ "flatfile", "plugin", "wordpress", - "exporter", + "cms", + "export", "flatfile-plugins", "category-transform" ], @@ -44,22 +45,20 @@ "license": "MIT", "dependencies": { "@flatfile/listener": "^1.0.5", - "@flatfile/plugin-record-hook": "^1.7.0", "@flatfile/api": "^1.9.15", - "axios": "^1.7.7" + "@flatfile/plugin-record-hook": "^1.7.0", + "axios": "^1.7.7", + "node-html-parser": "^6.1.13" }, "devDependencies": { "@flatfile/hooks": "^1.5.0", "@flatfile/rollup-config": "^0.1.1", "typescript": "^5.6.2", - "@types/node": "^22.7.0", - "rollup": "^4.22.4", - "jest": "^29.7.0", - "@types/jest": "^29.5.13" + "@types/node": "^22.7.0" }, "repository": { "type": "git", - "url": "https://github.com/YourUsername/flatfile-plugin-wordpress-exporter.git" + "url": "https://github.com/YourGitHubUsername/flatfile-plugin-wordpress-cms-export.git" }, "browserslist": [ "> 0.5%", diff --git a/validators/WordpressCMSExporter/rollup.config.mjs b/validators/WordpressCMSExporter/rollup.config.mjs index ed56cba03..f61298e83 100644 --- a/validators/WordpressCMSExporter/rollup.config.mjs +++ b/validators/WordpressCMSExporter/rollup.config.mjs @@ -1,45 +1,36 @@ import { buildConfig } from '@flatfile/rollup-config'; import typescript from '@rollup/plugin-typescript'; -import resolve from '@rollup/plugin-node-resolve'; -import commonjs from '@rollup/plugin-commonjs'; -import json from '@rollup/plugin-json'; const umdExternals = [ '@flatfile/api', - '@flatfile/hooks', '@flatfile/listener', - '@flatfile/util-common', '@flatfile/plugin-record-hook', - 'axios' + 'axios', + 'node-html-parser' ]; const config = buildConfig({ input: 'src/index.ts', // Assuming your main file is src/index.ts includeUmd: true, umdConfig: { - name: 'FlatfileWordPressExport', + name: 'WordpressCMSExport', external: umdExternals }, plugins: [ typescript({ tsconfig: './tsconfig.json', declaration: true, - declarationDir: 'dist/types' - }), - resolve({ - browser: true - }), - commonjs(), - json() + declarationDir: 'dist/types', + }) ] }); -// Add custom configuration for TypeScript handling +// Ensure all builds use the TypeScript plugin config.forEach(conf => { - if (conf.plugins) { - conf.plugins.unshift(typescript({ + if (!conf.plugins.find(plugin => plugin.name === 'typescript')) { + conf.plugins.push(typescript({ tsconfig: './tsconfig.json', - declaration: false + declaration: false, })); } }); diff --git a/validators/WordpressCMSExporter/src/index.ts b/validators/WordpressCMSExporter/src/index.ts index 0d6359a96..7e7bd809c 100644 --- a/validators/WordpressCMSExporter/src/index.ts +++ b/validators/WordpressCMSExporter/src/index.ts @@ -1,93 +1,163 @@ -import { FlatfileListener } from '@flatfile/listener' -import { recordHook } from '@flatfile/plugin-record-hook' +import { + FlatfileListener, + FlatfileEvent, + RecordObject, + SchemaField, +} from '@flatfile/listener' import api from '@flatfile/api' +import { recordHook } from '@flatfile/plugin-record-hook' import axios from 'axios' +import { parse } from 'node-html-parser' + +const WordpressCMSExport = (listener: FlatfileListener) => { + let wpConfig = { + apiUrl: '', + username: '', + password: '', + } -const listener = FlatfileListener.create((listener) => { listener.use( - recordHook('contacts', async (record, event) => { - const { jobId, environmentId, spaceId, sheetId } = event.context + recordHook('wordpress_posts', async (record: RecordObject) => { + const mappedFields = mapFields(record) + const defaultSettings = await getDefaultSettings() + const postWithDefaults = applyDefaultSettings( + mappedFields, + defaultSettings + ) + const preview = await previewPost(postWithDefaults) + record.set('preview', preview) + return record + }) + ) + listener.on( + 'job:ready', + { job: 'workbook:exportToWordpress' }, + async (event: FlatfileEvent) => { + const { jobId, workspaceId } = event.context + try { + await api.jobs.ack(jobId, { + info: 'Starting WordPress export', + progress: 10, + }) - // Field mapping - const fieldMapping = { - title: 'name', - content: 'description', - author: 'email', - categories: 'category', - tags: 'tags', - status: 'status', - custom_fields: 'custom_fields', - } + const records = await api.records.get(workspaceId, 'wordpress_posts') + const exportedRecords = await batchExport(records.data) - // Prepare post data - const postData = {} - for (const [wpField, ffField] of Object.entries(fieldMapping)) { - if (record.get(ffField)) { - if (wpField === 'categories' || wpField === 'tags') { - postData[wpField] = record - .get(ffField) - .split(',') - .map((item) => item.trim()) - } else if (wpField === 'custom_fields') { - postData[wpField] = JSON.parse(record.get(ffField) || '{}') - } else { - postData[wpField] = record.get(ffField) - } - } + await api.jobs.complete(jobId, { + outcome: { + message: `Successfully exported ${exportedRecords.length} posts to WordPress`, + }, + }) + } catch (error) { + await api.jobs.fail(jobId, { + outcome: { + message: `Export failed: ${error.message}`, + }, + }) } + } + ) - try { - // Get WordPress API credentials from Flatfile space metadata - const { data: space } = await api.spaces.get(spaceId) - const wpApiUrl = space.metadata.wpApiUrl - const wpApiKey = space.metadata.wpApiKey + listener.on('commit:created', async (event: FlatfileEvent) => { + const { workspaceId } = event.context + try { + await api.spaces.update(workspaceId, { + metadata: { + wpConfig: { + configured: false, + }, + }, + }) + } catch (error) { + console.error('Failed to update workspace metadata:', error) + } + }) - if (!wpApiUrl || !wpApiKey) { - throw new Error( - 'WordPress API credentials not found in space metadata' - ) - } + const mapFields = (record: RecordObject) => { + return { + title: record.get('post_title'), + content: record.get('post_content'), + status: record.get('post_status') || 'draft', + date: record.get('post_date'), + author: record.get('post_author'), + categories: record.get('post_categories'), + tags: record.get('post_tags'), + customFields: handleCustomFields(record), + } + } - const headers = { - Authorization: `Bearer ${wpApiKey}`, - 'Content-Type': 'application/json', - } + const handleCustomFields = (record: RecordObject) => { + const customFields = {} + const fieldKeys = Object.keys(record.value).filter((key) => + key.startsWith('custom_') + ) + fieldKeys.forEach((key) => { + customFields[key.replace('custom_', '')] = record.get(key) + }) + return customFields + } - // Check if post already exists (assuming 'name' is unique identifier) - const existingPosts = await axios.get( - `${wpApiUrl}/wp-json/wp/v2/posts?search=${postData.title}`, - { headers } - ) + const getDefaultSettings = async () => { + // Fetch default settings from WordPress or a configuration file + return { + status: 'draft', + author: 'admin', + categories: ['Uncategorized'], + tags: [], + } + } - let response - if (existingPosts.data.length > 0) { - // Update existing post - const postId = existingPosts.data[0].id - response = await axios.put( - `${wpApiUrl}/wp-json/wp/v2/posts/${postId}`, - postData, - { headers } - ) - } else { - // Create new post - response = await axios.post( - `${wpApiUrl}/wp-json/wp/v2/posts`, - postData, - { headers } - ) - } + const applyDefaultSettings = (postData, defaultSettings) => { + return { + ...defaultSettings, + ...postData, + } + } + + const previewPost = async (postData) => { + // Generate HTML preview of the post + const html = ` +

${postData.title}

+

Author: ${postData.author}

+
${postData.content}
+ ` + return parse(html).toString() + } - // Update record with WordPress post ID - record.set('wp_post_id', response.data.id) + const exportToWordpress = async (postData) => { + const { apiUrl, username, password } = wpConfig + try { + const response = await axios.post( + `${apiUrl}/wp-json/wp/v2/posts`, + postData, + { + auth: { + username, + password, + }, + } + ) + return response.data.id + } catch (error) { + throw new Error(`Failed to export post: ${error.message}`) + } + } - return record + const batchExport = async (records) => { + const exportedRecords = [] + for (const record of records) { + try { + const postData = mapFields(record) + const postId = await exportToWordpress(postData) + exportedRecords.push({ id: postId, title: postData.title }) } catch (error) { - console.error('Error exporting to WordPress:', error.message) - record.addError('wordpress_export', 'Failed to export to WordPress') - return record + console.error(`Failed to export record ${record.id}:`, error) } - }) - ) -}) + } + return exportedRecords + } + + return listener +} -export default listener +export default WordpressCMSExport