From e10ee8cd582c0f5d64479f13a5cfbbcb6b07505f Mon Sep 17 00:00:00 2001 From: David Boskovic Date: Wed, 7 Aug 2024 19:08:14 -0400 Subject: [PATCH] view mapped fields only plugin --- package-lock.json | 33 +++++-- plugins/view-mapped/CHANGELOG.md | 7 ++ plugins/view-mapped/README.md | 1 + plugins/view-mapped/package.json | 61 ++++++++++++ plugins/view-mapped/rollup.config.mjs | 5 + plugins/view-mapped/src/index.ts | 1 + plugins/view-mapped/src/view-mapped.ts | 123 +++++++++++++++++++++++++ 7 files changed, 224 insertions(+), 7 deletions(-) create mode 100644 plugins/view-mapped/CHANGELOG.md create mode 100644 plugins/view-mapped/README.md create mode 100644 plugins/view-mapped/package.json create mode 100644 plugins/view-mapped/rollup.config.mjs create mode 100644 plugins/view-mapped/src/index.ts create mode 100644 plugins/view-mapped/src/view-mapped.ts diff --git a/package-lock.json b/package-lock.json index 08a453810..2cbe25aee 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3449,9 +3449,9 @@ } }, "node_modules/@flatfile/api": { - "version": "1.9.5", - "resolved": "https://registry.npmjs.org/@flatfile/api/-/api-1.9.5.tgz", - "integrity": "sha512-yssldUquVfmsBN6ZWwMzwx2OpLiKgv7FHeXrzRf0qRg19CPQs58dvKq40WPiyDLPX2st7O1URHrTBaLm4xn53w==", + "version": "1.9.7", + "resolved": "https://registry.npmjs.org/@flatfile/api/-/api-1.9.7.tgz", + "integrity": "sha512-vEjKkro56404S+ec2Wa7tK1oE2LMJ9o6Kq87L1pxTWQfbgVvItBxfqk4A4YsKlEMDeUY93M5bEe/53IUQEim/A==", "dependencies": { "@flatfile/cross-env-config": "0.0.4", "@types/pako": "2.0.1", @@ -3737,6 +3737,10 @@ "resolved": "plugins/tsv-extractor", "link": true }, + "node_modules/@flatfile/plugin-view-mapped": { + "resolved": "plugins/view-mapped", + "link": true + }, "node_modules/@flatfile/plugin-webhook-egress": { "resolved": "plugins/webhook-egress", "link": true @@ -22719,7 +22723,7 @@ }, "plugins/delimiter-extractor": { "name": "@flatfile/plugin-delimiter-extractor", - "version": "2.1.1", + "version": "2.1.2", "license": "ISC", "dependencies": { "@flatfile/util-extractor": "^2.1.2", @@ -22830,7 +22834,7 @@ }, "plugins/json-extractor": { "name": "@flatfile/plugin-json-extractor", - "version": "0.8.1", + "version": "0.8.2", "license": "ISC", "dependencies": { "@flatfile/util-extractor": "^2.1.2" @@ -23050,6 +23054,21 @@ "@flatfile/listener": "^1.0.1" } }, + "plugins/view-mapped": { + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "@flatfile/api": "^1.9.7", + "@flatfile/listener": "^1.0.5" + }, + "devDependencies": { + "@flatfile/rollup-config": "0.1.1", + "@flatfile/utils-testing": "^0.2.0" + }, + "engines": { + "node": ">= 16" + } + }, "plugins/webhook-egress": { "name": "@flatfile/plugin-webhook-egress", "version": "1.3.2", @@ -23094,7 +23113,7 @@ }, "plugins/xlsx-extractor": { "name": "@flatfile/plugin-xlsx-extractor", - "version": "3.1.2", + "version": "3.1.3", "license": "ISC", "dependencies": { "@flatfile/util-extractor": "^2.1.2", @@ -23110,7 +23129,7 @@ }, "plugins/xml-extractor": { "name": "@flatfile/plugin-xml-extractor", - "version": "0.6.1", + "version": "0.6.2", "license": "ISC", "dependencies": { "@flatfile/util-extractor": "^2.1.2", diff --git a/plugins/view-mapped/CHANGELOG.md b/plugins/view-mapped/CHANGELOG.md new file mode 100644 index 000000000..ac15ba0d7 --- /dev/null +++ b/plugins/view-mapped/CHANGELOG.md @@ -0,0 +1,7 @@ +# @flatfile/plugin-job-handler + +## 1.0.1 + +### Patch Changes + +- initial version diff --git a/plugins/view-mapped/README.md b/plugins/view-mapped/README.md new file mode 100644 index 000000000..044a4824d --- /dev/null +++ b/plugins/view-mapped/README.md @@ -0,0 +1 @@ +# todo diff --git a/plugins/view-mapped/package.json b/plugins/view-mapped/package.json new file mode 100644 index 000000000..fb72214cb --- /dev/null +++ b/plugins/view-mapped/package.json @@ -0,0 +1,61 @@ +{ + "name": "@flatfile/plugin-view-mapped", + "version": "1.0.1", + "url": "https://github.com/FlatFilers/flatfile-plugins/tree/main/plugins/view-mapped", + "description": "A plugin for making the view post mapping show only mapped columns.", + "registryMetadata": { + "category": "core" + }, + "engines": { + "node": ">= 16" + }, + "browser": { + "./dist/index.cjs": "./dist/index.browser.cjs", + "./dist/index.mjs": "./dist/index.browser.mjs" + }, + "exports": { + "types": "./dist/index.d.ts", + "node": { + "import": "./dist/index.mjs", + "require": "./dist/index.cjs" + }, + "browser": { + "require": "./dist/index.browser.cjs", + "import": "./dist/index.browser.mjs" + }, + "default": "./dist/index.mjs" + }, + "main": "./dist/index.cjs", + "module": "./dist/index.mjs", + "source": "./src/index.ts", + "types": "./dist/index.d.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-plugins", + "category-core" + ], + "author": "Flatfile, Inc.", + "repository": { + "type": "git", + "url": "https://github.com/FlatFilers/flatfile-plugins.git", + "directory": "plugins/view-mapped" + }, + "license": "ISC", + "dependencies": { + "@flatfile/api": "^1.9.7", + "@flatfile/listener": "^1.0.5" + }, + "devDependencies": { + "@flatfile/rollup-config": "0.1.1", + "@flatfile/utils-testing": "^0.2.0" + } +} diff --git a/plugins/view-mapped/rollup.config.mjs b/plugins/view-mapped/rollup.config.mjs new file mode 100644 index 000000000..fafa813c6 --- /dev/null +++ b/plugins/view-mapped/rollup.config.mjs @@ -0,0 +1,5 @@ +import { buildConfig } from '@flatfile/rollup-config' + +const config = buildConfig({}) + +export default config diff --git a/plugins/view-mapped/src/index.ts b/plugins/view-mapped/src/index.ts new file mode 100644 index 000000000..98aad88ab --- /dev/null +++ b/plugins/view-mapped/src/index.ts @@ -0,0 +1 @@ +export * from './view-mapped' diff --git a/plugins/view-mapped/src/view-mapped.ts b/plugins/view-mapped/src/view-mapped.ts new file mode 100644 index 000000000..2e595bd9c --- /dev/null +++ b/plugins/view-mapped/src/view-mapped.ts @@ -0,0 +1,123 @@ +import api from '@flatfile/api' +import type { FlatfileListener } from '@flatfile/listener' + +/** + * This plugin allows you to make the post-mapping sheet only display mapped data + */ +export function viewMappedPlugin() { + return (listener: FlatfileListener) => { + // Defining what needs to be done when Flatfile is done mapping columns based on user input during the Mapping stage of the import process + listener.on( + 'job:completed', + { job: 'workbook:map' }, + async ({ context: { jobId, workbookId } }) => { + // Creating a custom job that we will use in the next listener to ensure users only see mapped fields in the table + await api.jobs.create({ + type: 'workbook', + operation: 'viewMappedFieldsOnly', + source: workbookId, + // This ensures that our custom job will execute automatically when the "job:ready" event of the listener below triggers + trigger: 'immediate', + // This ensures that users are not able to interact with records in the table until it is updated to only show mapped fields + mode: 'foreground', + // This ensures that in the next listener we are able to access the jobId of the mapping job specifically, and not just the jobId of this custom job + input: { mappingJobId: jobId }, + }) + } + ) + + // Defining what needs to be done when our custom job triggers. Because we create it when mapping job completes, this is when this job will begin executing + listener + .filter({ job: 'workbook:viewMappedFieldsOnly' }) + .on('job:ready', async ({ context: { jobId, workbookId } }) => { + try { + // First, we acknowledge the job + await api.jobs.ack(jobId, { + info: 'Updating the table to only view mapped fields', + progress: 10, + }) + + // Retrieving the info on the custom job we created in the listener above, and storing that info in its own "customJobInfo" variable + const customJobInfo = await api.jobs.get(jobId) + + // From "customJobInfo" variable, retrieving the jobId specifically of the mapping job that completed, and storing it in its own "mappingJobId" variable + const mappingJobId = customJobInfo.data.input.mappingJobId + + // Obtaining the mapping job's execution plan to later extract "fieldMapping" out of it, which tells us which fields were mapped in the Matching step + const jobPlan = await api.jobs.getExecutionPlan(mappingJobId) + + // Initializing an empty array to store the keys of the mapped fields + const mappedFields = [] + + // Iterating through all destination fields that are mapped and extracting their field keys. Then, pushing keys of mapped fields to the "mappedFields" variable + for (let i = 0; i < jobPlan.data.plan.fieldMapping.length; i++) { + const destinationFieldKey = + jobPlan.data.plan.fieldMapping[i].destinationField.key + + mappedFields.push(destinationFieldKey) + } + // Making an API call to only get the "data" property out of the response, and saving it as its own "fetchedWorkbook" variable + // We need to make this API call and cannot just use what's inside of "workbookOne" because we need data in a specific format + const { data: workbook } = await api.workbooks.get(workbookId) + + // Looping through all sheets of the Workbook One. For all fields that are mapped, updating those fields' metadata to "{mapped: true}" + workbook.sheets.forEach((sheet) => { + sheet.config.fields.forEach((field) => { + if (mappedFields.includes(field.key)) { + field.metadata = { mapped: true } + } + }) + }) + + // Looping over each sheet in "workbook" and filtering for fields with metadata "mapped: true". Saving mapped fields per each sheet inside of "filteredWorkbookFields" varibable + const filteredWorkbookFields = workbook.sheets.map((sheet) => { + const fields = sheet.config.fields.filter( + (field) => field.metadata && field.metadata.mapped === true + ) + return fields.length > 0 ? fields : null + }) + + // Updating each sheet in a workbook to only contain fields that a user mapped. This ensures that when the table with data loads, only mapped fields will be displayed + await api.workbooks.update(workbookId, { + // Keeping other non-sheet elements of the workbook untouched (Workbook name, its Submit action, etc) + ...workbook, + + // Mapping over each sheet to update each to only contain fields that are inside of "filteredWorkbookFields" variable (that have metadata "{mapped: true})" + sheets: workbook.sheets.map((sheet, index) => { + const mappedWorkbookFields = filteredWorkbookFields[index] + + // If there are no mapped fields, returning the original sheet structure + if (!mappedWorkbookFields) { + return sheet + } + + // If there are mapped fields, returning all properties of the original sheet but updating the "fields" property to the mapped fields + return { + ...sheet, + config: { + ...sheet.config, + fields: mappedWorkbookFields, + }, + } + }), + }) + + // Completing the job with an appropriate message to the user + await api.jobs.complete(jobId, { + outcome: { + message: 'Table update complete. Please audit the data', + acknowledge: true, + }, + }) + } catch (error) { + // If something goes wrong while executing the custom job, we fail the job with a message on what next steps to take + await api.jobs.fail(jobId, { + outcome: { + message: + 'An error occured while updating the workbook. See Event Logs.', + }, + }) + } + }) + } +}