UNPKG

@flatfile/plugin-view-mapped

Version:

A plugin for making the view post mapping show only mapped columns.

1 lines 12.7 kB
{"version":3,"sources":["../src/view-mapped.ts"],"names":["api","FlatfileClient","sleep","ms","resolve","viewMappedPlugin","options","listener","event","jobId","destinationSheetId","jobHandler","tick","workbookId","mappingJobId","jobPlan","mappedFields","i","destinationFieldKey","workbook","sheet","field","filteredWorkbookFields","fields","isRequired","constraint","sheets","index","mappedWorkbookFields","hasUncompletedCommits","commits","error","logError"],"mappings":"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","file":"index.browser.cjs","sourcesContent":["import { type Flatfile, FlatfileClient } from '@flatfile/api'\nimport type { FlatfileListener } from '@flatfile/listener'\nimport { jobHandler } from '@flatfile/plugin-job-handler'\nimport { logError } from '@flatfile/util-common'\n\nconst api = new FlatfileClient()\n\nconst sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms))\n\ninterface ViewMappedOptions {\n /**\n * If true, the plugin will skip removal of required fields\n */\n keepRequiredFields?: boolean\n}\n\n/**\n * This plugin allows you to make the post-mapping sheet only display mapped data\n */\nexport function viewMappedPlugin(options: ViewMappedOptions) {\n return (listener: FlatfileListener) => {\n // Defining what needs to be done when Flatfile is done mapping columns based on user input during the Mapping stage of the import process\n listener.on('job:completed', { job: 'workbook:map' }, async (event) => {\n const { jobId } = event.context\n\n // 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\n const jobPlan = await api.jobs.getExecutionPlan(jobId)\n\n const destinationSheetId = (\n jobPlan.data.job.config as Flatfile.MappingProgramJobConfig\n ).destinationSheetId\n\n // Creating a custom job that we will use in the next listener to ensure users only see mapped fields in the table\n await api.jobs.create({\n // We are creating a sheet level job so that the record counts will recompute\n type: 'sheet',\n operation: 'viewMappedFieldsOnly',\n source: destinationSheetId,\n // This ensures that our custom job will execute automatically when the \"job:ready\" event of the listener below triggers\n trigger: 'immediate',\n // This ensures that users are not able to interact with records in the table until it is updated to only show mapped fields\n mode: 'foreground',\n // 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\n input: { mappingJobId: jobId },\n })\n })\n\n // 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\n listener.use(\n jobHandler('sheet:viewMappedFieldsOnly', async (event, tick) => {\n const { jobId, workbookId } = event.context\n\n try {\n // First, we acknowledge the job\n await tick(10, 'plugins.viewMapped.updatingTable')\n\n // Retrieving the info on the custom job we created in the listener above, and storing that info in its own \"customJobInfo\" variable\n const customJobInfo = await api.jobs.get(jobId)\n\n // From \"customJobInfo\" variable, retrieving the jobId specifically of the mapping job that completed, and storing it in its own \"mappingJobId\" variable\n const mappingJobId = customJobInfo.data.input.mappingJobId\n\n // 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\n const jobPlan = await api.jobs.getExecutionPlan(mappingJobId)\n\n const destinationSheetId = (\n jobPlan.data.job.config as Flatfile.MappingProgramJobConfig\n ).destinationSheetId\n\n // Initializing an empty array to store the keys of the mapped fields\n const mappedFields = []\n\n // Iterating through all destination fields that are mapped and extracting their field keys. Then, pushing keys of mapped fields to the \"mappedFields\" variable\n for (let i = 0; i < jobPlan.data.plan.fieldMapping.length; i++) {\n const destinationFieldKey =\n jobPlan.data.plan.fieldMapping[i].destinationField.key\n\n mappedFields.push(destinationFieldKey)\n }\n\n await tick(30, 'plugins.viewMapped.updatingTable')\n\n // Making an API call to only get the \"data\" property out of the response, and saving it as its own \"fetchedWorkbook\" variable\n // We need to make this API call and cannot just use what's inside of \"workbookOne\" because we need data in a specific format\n const { data: workbook } = await api.workbooks.get(workbookId)\n\n // If trackChanges is not enabled, we skip the rest of the job. This configuration is required to provide the plugins with the\n // awareness that all hooks have run. Without it, we run into a race condition between the hook and the Workbook update. If the\n // Workbook update runs before the hook, the hook will not be able to update the Workbook.\n if (!workbook.settings?.trackChanges) {\n console.log('Skipping because trackChanges is not enabled')\n return\n }\n\n // Looping through all sheets of the Workbook One. For all fields that are mapped, updating those fields' metadata to \"{mapped: true}\"\n workbook.sheets.forEach((sheet) => {\n if (sheet.id === destinationSheetId) {\n sheet.config.fields.forEach((field) => {\n if (mappedFields.includes(field.key)) {\n field.metadata = { mapped: true }\n }\n })\n }\n })\n\n // Looping over each sheet in \"workbook\" and filtering for fields with metadata \"mapped: true\". Saving mapped fields per each sheet inside of \"filteredWorkbookFields\" varibable\n const filteredWorkbookFields = workbook.sheets.map((sheet) => {\n const fields = sheet.config.fields.filter((field) => {\n // Keep mapped fields\n if (field.metadata?.mapped === true) return true\n\n // Handle required fields based on keepRequiredFields option\n const isRequired = field.constraints?.some(\n (constraint) => constraint.type === 'required'\n )\n return options.keepRequiredFields && isRequired\n })\n return fields.length > 0 ? fields : null\n })\n\n await tick(50, 'plugins.viewMapped.halfway')\n\n const sheets = workbook.sheets.map((sheet, index) => {\n const mappedWorkbookFields = filteredWorkbookFields[index]\n\n // If there are no mapped fields, returning the original sheet structure\n if (!mappedWorkbookFields) {\n return sheet\n }\n\n // If there are mapped fields, returning all properties of the original sheet but updating the \"fields\" property to the mapped fields\n return {\n ...sheet,\n config: {\n ...sheet.config,\n fields: mappedWorkbookFields,\n },\n }\n })\n\n await tick(80, 'plugins.viewMapped.almostDone')\n\n // Check that all commits are completed before running this job\n let hasUncompletedCommits = true\n do {\n const { data: commits } = await api.sheets.getSheetCommits(\n destinationSheetId,\n {\n completed: false,\n }\n )\n console.log(`Waiting on ${commits.length} commits to complete...`)\n hasUncompletedCommits = commits.length > 0\n if (hasUncompletedCommits) {\n // We wait for 200ms between each check to avoid making too many requests to the API\n await sleep(200)\n }\n } while (hasUncompletedCommits)\n // For good measure, sleep for 300ms to ensure that the workbook is updated before the next job is run\n await sleep(300)\n\n // 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\n await api.workbooks.update(workbookId, {\n // Keeping other non-sheet elements of the workbook untouched (Workbook name, its Submit action, etc)\n ...workbook,\n\n // Mapping over each sheet to update each to only contain fields that are inside of \"filteredWorkbookFields\" variable (that have metadata \"{mapped: true})\"\n sheets,\n })\n\n // Completing the job with an appropriate message to the user\n return {\n outcome: {\n message: 'plugins.viewMapped.complete',\n acknowledge: false,\n },\n }\n } catch (error) {\n // If something goes wrong while executing the custom job, we fail the job with a message on what next steps to take\n logError(\n '@flatfile/plugin-view-mapped',\n JSON.stringify(error, null, 2)\n )\n throw new Error('plugins.viewMapped.error')\n }\n })\n )\n }\n}\n"]}