diff --git a/package-lock.json b/package-lock.json index a5abc975a..18530b0eb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -81,6 +81,7 @@ "flatfilers/playground": { "name": "@private/playground", "version": "0.0.0", + "extraneous": true, "license": "ISC", "dependencies": { "@flatfile/api": "^1.9.19", @@ -3188,6 +3189,10 @@ "resolved": "plugins/tsv-extractor", "link": true }, + "node_modules/@flatfile/plugin-validate-boolean": { + "resolved": "validate/boolean", + "link": true + }, "node_modules/@flatfile/plugin-validate-isbn": { "resolved": "validate/isbn", "link": true @@ -20206,14 +20211,14 @@ }, "plugins/autocast": { "name": "@flatfile/plugin-autocast", - "version": "2.0.0", + "version": "2.0.1", "license": "ISC", "dependencies": { "@flatfile/hooks": "^1.4.1", - "@flatfile/util-common": "^1.4.0" + "@flatfile/util-common": "^1.4.1" }, "devDependencies": { - "@flatfile/plugin-record-hook": "^1.7.0", + "@flatfile/plugin-record-hook": "^1.7.1", "@flatfile/rollup-config": "0.1.1" }, "engines": { @@ -20222,19 +20227,19 @@ "peerDependencies": { "@flatfile/api": "^1.9.19", "@flatfile/listener": "^1.1.0", - "@flatfile/plugin-record-hook": "^1.7.0" + "@flatfile/plugin-record-hook": "^1.7.1" } }, "plugins/automap": { "name": "@flatfile/plugin-automap", - "version": "0.5.0", + "version": "0.5.1", "license": "ISC", "dependencies": { "@flatfile/common-plugin-utils": "^1.0.2", "remeda": "^1.23.0" }, "devDependencies": { - "@flatfile/utils-testing": "^0.3.0" + "@flatfile/utils-testing": "^0.3.1" }, "engines": { "node": ">= 16" @@ -20246,10 +20251,10 @@ }, "plugins/constraints": { "name": "@flatfile/plugin-constraints", - "version": "3.0.0", + "version": "3.0.1", "license": "ISC", "devDependencies": { - "@flatfile/plugin-record-hook": "^1.7.0", + "@flatfile/plugin-record-hook": "^1.7.1", "@flatfile/rollup-config": "0.1.1" }, "engines": { @@ -20258,16 +20263,16 @@ "peerDependencies": { "@flatfile/api": "^1.9.19", "@flatfile/listener": "^1.1.0", - "@flatfile/plugin-record-hook": "^1.7.0" + "@flatfile/plugin-record-hook": "^1.7.1" } }, "plugins/dedupe": { "name": "@flatfile/plugin-dedupe", - "version": "1.2.0", + "version": "1.2.1", "license": "ISC", "dependencies": { - "@flatfile/plugin-job-handler": "^0.6.0", - "@flatfile/util-common": "^1.4.0" + "@flatfile/plugin-job-handler": "^0.6.1", + "@flatfile/util-common": "^1.4.1" }, "devDependencies": { "@faker-js/faker": "^7.6.0", @@ -20283,10 +20288,10 @@ }, "plugins/delimiter-extractor": { "name": "@flatfile/plugin-delimiter-extractor", - "version": "2.2.1", + "version": "2.2.2", "license": "ISC", "dependencies": { - "@flatfile/util-extractor": "^2.1.2", + "@flatfile/util-extractor": "^2.1.7", "papaparse": "^5.4.1", "remeda": "^1.14.0" }, @@ -20303,7 +20308,7 @@ }, "plugins/dxp-configure": { "name": "@flatfile/plugin-dxp-configure", - "version": "1.2.0", + "version": "1.2.1", "license": "ISC", "devDependencies": { "@flatfile/configure": "^1.0.1", @@ -20319,11 +20324,11 @@ }, "plugins/export-workbook": { "name": "@flatfile/plugin-export-workbook", - "version": "1.0.0", + "version": "1.0.1", "license": "ISC", "devDependencies": { - "@flatfile/plugin-job-handler": "^0.6.0", - "@flatfile/util-common": "^1.4.0" + "@flatfile/plugin-job-handler": "^0.6.1", + "@flatfile/util-common": "^1.4.1" }, "engines": { "node": ">= 16" @@ -20331,15 +20336,15 @@ "peerDependencies": { "@flatfile/api": "^1.9.19", "@flatfile/listener": "^1.1.0", - "@flatfile/plugin-job-handler": "^0.6.0", - "@flatfile/util-common": "^1.4.0", + "@flatfile/plugin-job-handler": "^0.6.1", + "@flatfile/util-common": "^1.4.1", "remeda": "^1.14.0", "xlsx": "https://cdn.sheetjs.com/xlsx-0.20.2/xlsx-0.20.2.tgz" } }, "plugins/foreign-db-extractor": { "name": "@flatfile/plugin-foreign-db-extractor", - "version": "0.2.0", + "version": "0.2.1", "license": "ISC", "dependencies": { "cross-fetch": "^4.0.0", @@ -20358,10 +20363,10 @@ }, "plugins/graphql-schema": { "name": "@flatfile/plugin-graphql-schema", - "version": "1.3.0", + "version": "1.3.1", "license": "ISC", "dependencies": { - "@flatfile/plugin-space-configure": "^0.6.0", + "@flatfile/plugin-space-configure": "^0.6.1", "change-case": "^5.4.3", "cross-fetch": "^4.0.0", "graphql": "^16.8.1" @@ -20375,14 +20380,14 @@ }, "plugins/job-handler": { "name": "@flatfile/plugin-job-handler", - "version": "0.6.0", + "version": "0.6.1", "license": "ISC", "dependencies": { - "@flatfile/util-common": "^1.4.0" + "@flatfile/util-common": "^1.4.1" }, "devDependencies": { "@flatfile/rollup-config": "0.1.1", - "@flatfile/utils-testing": "^0.3.0" + "@flatfile/utils-testing": "^0.3.1" }, "engines": { "node": ">= 16" @@ -20408,10 +20413,10 @@ }, "plugins/json-schema": { "name": "@flatfile/plugin-convert-json-schema", - "version": "0.4.0", + "version": "0.4.1", "license": "ISC", "dependencies": { - "@flatfile/plugin-space-configure": "^0.6.0", + "@flatfile/plugin-space-configure": "^0.6.1", "cross-fetch": "^4.0.0" }, "engines": { @@ -20424,11 +20429,11 @@ }, "plugins/merge-connection": { "name": "@flatfile/plugin-connect-via-merge", - "version": "0.4.0", + "version": "0.4.1", "license": "ISC", "dependencies": { - "@flatfile/plugin-convert-openapi-schema": "^0.3.0", - "@flatfile/plugin-job-handler": "^0.6.0", + "@flatfile/plugin-convert-openapi-schema": "^0.3.1", + "@flatfile/plugin-job-handler": "^0.6.1", "@mergeapi/merge-node-client": "^1.0.4" }, "engines": { @@ -20441,10 +20446,10 @@ }, "plugins/openapi-schema": { "name": "@flatfile/plugin-convert-openapi-schema", - "version": "0.3.0", + "version": "0.3.1", "license": "ISC", "dependencies": { - "@flatfile/plugin-space-configure": "^0.6.0", + "@flatfile/plugin-space-configure": "^0.6.1", "cross-fetch": "^4.0.0" }, "devDependencies": { @@ -20460,11 +20465,11 @@ }, "plugins/pdf-extractor": { "name": "@flatfile/plugin-pdf-extractor", - "version": "0.3.0", + "version": "0.3.1", "license": "ISC", "dependencies": { - "@flatfile/util-common": "^1.4.0", - "@flatfile/util-file-buffer": "^0.4.0", + "@flatfile/util-common": "^1.4.1", + "@flatfile/util-file-buffer": "^0.4.1", "cross-fetch": "^4.0.0", "form-data": "^4.0.0", "fs-extra": "^11.1.1", @@ -20512,7 +20517,7 @@ }, "plugins/psv-extractor": { "name": "@flatfile/plugin-psv-extractor", - "version": "1.9.0", + "version": "1.9.1", "license": "ISC", "engines": { "node": ">= 16" @@ -20524,10 +20529,10 @@ }, "plugins/record-hook": { "name": "@flatfile/plugin-record-hook", - "version": "1.7.0", + "version": "1.7.1", "license": "ISC", "dependencies": { - "@flatfile/util-common": "^1.4.0" + "@flatfile/util-common": "^1.4.1" }, "devDependencies": { "@flatfile/rollup-config": "0.1.1" @@ -20543,11 +20548,11 @@ }, "plugins/rollout": { "name": "@flatfile/plugin-rollout", - "version": "1.1.0", + "version": "1.1.1", "license": "ISC", "dependencies": { - "@flatfile/plugin-job-handler": "^0.6.0", - "@flatfile/util-common": "^1.4.0", + "@flatfile/plugin-job-handler": "^0.6.1", + "@flatfile/util-common": "^1.4.1", "async": "^3.2.5", "modern-async": "^2.0.0" }, @@ -20564,15 +20569,15 @@ }, "plugins/space-configure": { "name": "@flatfile/plugin-space-configure", - "version": "0.6.0", + "version": "0.6.1", "license": "ISC", "dependencies": { - "@flatfile/plugin-job-handler": "^0.6.0" + "@flatfile/plugin-job-handler": "^0.6.1" }, "devDependencies": { "@flatfile/api": "^1.9.19", "@flatfile/rollup-config": "0.1.1", - "@flatfile/utils-testing": "^0.3.0" + "@flatfile/utils-testing": "^0.3.1" }, "engines": { "node": ">= 16" @@ -20584,11 +20589,11 @@ }, "plugins/sql-ddl-converter": { "name": "@flatfile/plugin-convert-sql-ddl", - "version": "0.2.0", + "version": "0.2.1", "license": "ISC", "dependencies": { - "@flatfile/plugin-convert-json-schema": "^0.4.0", - "@flatfile/plugin-space-configure": "^0.6.0", + "@flatfile/plugin-convert-json-schema": "^0.4.1", + "@flatfile/plugin-space-configure": "^0.6.1", "sql-ddl-to-json-schema": "^4.1.0" }, "engines": { @@ -20601,7 +20606,7 @@ }, "plugins/tsv-extractor": { "name": "@flatfile/plugin-tsv-extractor", - "version": "1.8.0", + "version": "1.8.1", "license": "ISC", "engines": { "node": ">= 16" @@ -20613,7 +20618,7 @@ }, "plugins/view-mapped": { "name": "@flatfile/plugin-view-mapped", - "version": "1.0.2", + "version": "1.0.3", "license": "ISC", "dependencies": { "@flatfile/api": "^1.9.19", @@ -20621,7 +20626,7 @@ }, "devDependencies": { "@flatfile/rollup-config": "0.1.1", - "@flatfile/utils-testing": "^0.3.0" + "@flatfile/utils-testing": "^0.3.1" }, "engines": { "node": ">= 16" @@ -20629,17 +20634,17 @@ }, "plugins/webhook-egress": { "name": "@flatfile/plugin-webhook-egress", - "version": "1.4.0", + "version": "1.4.1", "license": "ISC", "dependencies": { - "@flatfile/plugin-job-handler": "^0.6.0", - "@flatfile/util-common": "^1.4.0", - "@flatfile/util-response-rejection": "^1.4.0", + "@flatfile/plugin-job-handler": "^0.6.1", + "@flatfile/util-common": "^1.4.1", + "@flatfile/util-response-rejection": "^1.4.1", "cross-fetch": "^4.0.0" }, "devDependencies": { "@flatfile/rollup-config": "0.1.1", - "@flatfile/utils-testing": "^0.3.0", + "@flatfile/utils-testing": "^0.3.1", "jest-fetch-mock": "^3.0.3" }, "engines": { @@ -20652,7 +20657,7 @@ }, "plugins/webhook-event-forwarder": { "name": "@flatfile/plugin-webhook-event-forwarder", - "version": "0.4.0", + "version": "0.4.1", "license": "ISC", "dependencies": { "cross-fetch": "^4.0.0" @@ -20671,10 +20676,10 @@ }, "plugins/xlsx-extractor": { "name": "@flatfile/plugin-xlsx-extractor", - "version": "3.2.1", + "version": "3.2.2", "license": "ISC", "dependencies": { - "@flatfile/util-extractor": "^2.1.5", + "@flatfile/util-extractor": "^2.1.7", "remeda": "^1.14.0", "xlsx": "https://cdn.sheetjs.com/xlsx-0.20.2/xlsx-0.20.2.tgz" }, @@ -20687,10 +20692,10 @@ }, "plugins/xml-extractor": { "name": "@flatfile/plugin-xml-extractor", - "version": "0.7.0", + "version": "0.7.1", "license": "ISC", "dependencies": { - "@flatfile/util-extractor": "^2.1.6", + "@flatfile/util-extractor": "^2.1.7", "remeda": "^1.24.0", "xml-json-format": "^1.0.8" }, @@ -20703,16 +20708,16 @@ }, "plugins/yaml-schema": { "name": "@flatfile/plugin-convert-yaml-schema", - "version": "0.3.0", + "version": "0.3.1", "license": "ISC", "dependencies": { - "@flatfile/plugin-convert-json-schema": "^0.4.0", - "@flatfile/plugin-space-configure": "^0.6.0", + "@flatfile/plugin-convert-json-schema": "^0.4.1", + "@flatfile/plugin-space-configure": "^0.6.1", "cross-fetch": "^4.0.0", "js-yaml": "^4.1.0" }, "devDependencies": { - "@flatfile/utils-testing": "^0.3.0", + "@flatfile/utils-testing": "^0.3.1", "express": "^4.18.2", "jest-fetch-mock": "^3.0.3" }, @@ -20740,17 +20745,17 @@ }, "plugins/zip-extractor": { "name": "@flatfile/plugin-zip-extractor", - "version": "0.6.0", + "version": "0.6.1", "license": "ISC", "dependencies": { - "@flatfile/plugin-job-handler": "^0.6.0", - "@flatfile/util-common": "^1.4.0", - "@flatfile/util-file-buffer": "^0.4.0", + "@flatfile/plugin-job-handler": "^0.6.1", + "@flatfile/util-common": "^1.4.1", + "@flatfile/util-file-buffer": "^0.4.1", "adm-zip": "^0.5.10", "modern-async": "^2.0.0" }, "devDependencies": { - "@flatfile/utils-testing": "^0.3.0", + "@flatfile/utils-testing": "^0.3.1", "@types/adm-zip": "^0.4.3" }, "engines": { @@ -20771,7 +20776,7 @@ }, "utils/common": { "name": "@flatfile/util-common", - "version": "1.4.0", + "version": "1.4.1", "license": "ISC", "dependencies": { "@flatfile/cross-env-config": "^0.0.6", @@ -20795,11 +20800,11 @@ }, "utils/extractor": { "name": "@flatfile/util-extractor", - "version": "2.1.6", + "version": "2.1.7", "license": "ISC", "dependencies": { - "@flatfile/util-common": "^1.4.0", - "@flatfile/util-file-buffer": "^0.4.0" + "@flatfile/util-common": "^1.4.1", + "@flatfile/util-file-buffer": "^0.4.1" }, "engines": { "node": ">= 16" @@ -20811,7 +20816,7 @@ }, "utils/fetch-schema": { "name": "@flatfile/util-fetch-schema", - "version": "0.2.3", + "version": "0.2.4", "license": "ISC", "dependencies": { "cross-fetch": "^4.0.0" @@ -20825,7 +20830,7 @@ }, "utils/file-buffer": { "name": "@flatfile/util-file-buffer", - "version": "0.4.0", + "version": "0.4.1", "license": "ISC", "engines": { "node": ">= 16" @@ -20837,10 +20842,10 @@ }, "utils/response-rejection": { "name": "@flatfile/util-response-rejection", - "version": "1.4.0", + "version": "1.4.1", "license": "ISC", "dependencies": { - "@flatfile/util-common": "^1.4.0" + "@flatfile/util-common": "^1.4.1" }, "devDependencies": { "@flatfile/rollup-config": "0.1.1" @@ -20854,7 +20859,7 @@ }, "utils/testing": { "name": "@flatfile/utils-testing", - "version": "0.3.0", + "version": "0.3.1", "license": "ISC", "dependencies": { "@flatfile/api": "^1.9.19", @@ -20867,10 +20872,23 @@ "node": ">= 16" } }, + "validate/boolean": { + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "@flatfile/plugin-record-hook": "^1.7.0" + }, + "devDependencies": { + "@flatfile/rollup-config": "^0.1.1" + }, + "peerDependencies": { + "@flatfile/listener": "^1.0.5" + } + }, "validate/isbn": { "name": "@flatfile/plugin-validate-isbn", - "version": "1.0.0", - "license": "MIT", + "version": "0.0.0", + "license": "ISC", "dependencies": { "@flatfile/plugin-record-hook": "^1.7.0", "isbn3": "^1.2.0" diff --git a/validate/boolean/README.MD b/validate/boolean/README.MD new file mode 100644 index 000000000..a1eab871f --- /dev/null +++ b/validate/boolean/README.MD @@ -0,0 +1,69 @@ + +# @flatfile/plugin-validate-boolean + +The `@flatfile/plugin-validate-boolean` plugin provides comprehensive boolean validation capabilities, offering flexible configuration options to handle various boolean representations across different languages and use cases. + +**Event Type:** +`listener.on('commit:created')` + + + +## Features + +- Supports both strict and truthy boolean validation +- Multi-language support (English, Spanish, French, German) +- Custom mapping for boolean values +- Case-sensitive and case-insensitive options +- Configurable null value handling +- Option to convert non-boolean values +- Custom error messages +- Default value setting + +## Installation + +To install the plugin, run the following command: + +```bash +npm install @flatfile/plugin-validate-boolean +``` + +## Example Usage + +```javascript +import { validateBoolean } from '@flatfile/plugin-validate-boolean'; + +const booleanValidator = validateBoolean({ + fields: ['isActive', 'hasSubscription'], + validationType: 'truthy', + language: 'en', + handleNull: 'false', + convertNonBoolean: true +}); + +listener.use(booleanValidator); +``` + +## Configuration + +The `BooleanValidator` function accepts a configuration object with the following properties: + +- `fields`: An array of field names to validate +- `validationType`: 'strict' or 'truthy' +- `customMapping`: A custom mapping of string values to boolean +- `caseSensitive`: Whether the validation should be case-sensitive +- `handleNull`: How to handle null values ('error', 'false', 'true', or 'skip') +- `convertNonBoolean`: Whether to convert non-boolean values to boolean +- `language`: The language for predefined mappings ('en', 'es', 'fr', 'de') +- `customErrorMessages`: Custom error messages for different scenarios +- `defaultValue`: A default value to use for invalid inputs + +## Behavior + +1. **Strict Validation**: Only accepts 'true', 'false', true, or false as valid inputs. +2. **Truthy Validation**: Accepts various representations of true/false, including language-specific terms. +3. **Null Handling**: Configurable behavior for null or undefined values. +4. **Non-Boolean Conversion**: Option to convert non-boolean values to boolean. +5. **Error Handling**: Adds errors or info messages to the record for invalid inputs. +6. **Default Value**: Option to set a default value for invalid inputs instead of raising an error. + +The plugin can be used either as a RecordHook or as an external constraint, providing flexibility in integration with your Flatfile setup. diff --git a/validate/boolean/metadata.json b/validate/boolean/metadata.json new file mode 100644 index 000000000..f9c4a3a3d --- /dev/null +++ b/validate/boolean/metadata.json @@ -0,0 +1,98 @@ +{ + "timestamp": "2024-10-01T04-33-41-969Z", + "task": "Create a Boolean Validator Flatfile Listener plugin:\n - Add configuration for what fields are booleans\n - Implement strict boolean validation (true/false only)\n - Add support for truthy/falsy value validation\n - Allow custom truthy/falsy value mapping (e.g., 'yes'/'no', '1'/'0')\n - Implement case-insensitive boolean string matching\n - Add options for handling null/undefined values\n - Implement conversion of non-boolean types to boolean\n - Add support for multi-language boolean representations\n - Implement custom error messages for invalid boolean values\n - Add options for default values when conversion fails\n - Export recordHook and external constraint versions", + "summary": "This code implements a Boolean Validator plugin for Flatfile, providing both recordHook and external constraint versions. The plugin validates boolean fields in records, supporting strict and truthy validation types, custom mappings, and multi-language support.", + "steps": [ + [ + "Retrieve information about Flatfile Listeners and the Record Hook plugin to understand the structure and best practices.\n", + "#E1", + "PineconeAssistant", + "Provide information about Flatfile Listeners and the Record Hook plugin, including their structure and best practices", + "Plan: Retrieve information about Flatfile Listeners and the Record Hook plugin to understand the structure and best practices.\n#E1 = PineconeAssistant[Provide information about Flatfile Listeners and the Record Hook plugin, including their structure and best practices]" + ], + [ + "Create the basic structure of the Boolean Validator plugin, including configuration options for boolean fields and validation types.\n", + "#E2", + "LLM", + "Create a basic structure for a Flatfile Listener plugin named BooleanValidator with configuration options for boolean fields and validation types (strict, truthy/falsy) based on the information in #E1", + "Plan: Create the basic structure of the Boolean Validator plugin, including configuration options for boolean fields and validation types.\n#E2 = LLM[Create a basic structure for a Flatfile Listener plugin named BooleanValidator with configuration options for boolean fields and validation types (strict, truthy/falsy) based on the information in #E1]" + ], + [ + "Implement strict boolean validation and truthy/falsy value validation.\n", + "#E3", + "LLM", + "Extend the BooleanValidator plugin from #E2 to include implementations for strict boolean validation (true/false only) and truthy/falsy value validation", + "Plan: Implement strict boolean validation and truthy/falsy value validation.\n#E3 = LLM[Extend the BooleanValidator plugin from #E2 to include implementations for strict boolean validation (true/false only) and truthy/falsy value validation]" + ], + [ + "Add support for custom truthy/falsy value mapping and case-insensitive boolean string matching.\n", + "#E4", + "LLM", + "Enhance the BooleanValidator plugin from #E3 to support custom truthy/falsy value mapping (e.g., 'yes'/'no', '1'/'0') and implement case-insensitive boolean string matching", + "Plan: Add support for custom truthy/falsy value mapping and case-insensitive boolean string matching.\n#E4 = LLM[Enhance the BooleanValidator plugin from #E3 to support custom truthy/falsy value mapping (e.g., 'yes'/'no', '1'/'0') and implement case-insensitive boolean string matching]" + ], + [ + "Implement options for handling null/undefined values and conversion of non-boolean types to boolean.\n", + "#E5", + "LLM", + "Add functionality to the BooleanValidator plugin from #E4 to handle null/undefined values and implement conversion of non-boolean types to boolean", + "Plan: Implement options for handling null/undefined values and conversion of non-boolean types to boolean.\n#E5 = LLM[Add functionality to the BooleanValidator plugin from #E4 to handle null/undefined values and implement conversion of non-boolean types to boolean]" + ], + [ + "Add support for multi-language boolean representations and custom error messages for invalid boolean values.\n", + "#E6", + "LLM", + "Extend the BooleanValidator plugin from #E5 to support multi-language boolean representations and implement custom error messages for invalid boolean values", + "Plan: Add support for multi-language boolean representations and custom error messages for invalid boolean values.\n#E6 = LLM[Extend the BooleanValidator plugin from #E5 to support multi-language boolean representations and implement custom error messages for invalid boolean values]" + ], + [ + "Implement options for default values when conversion fails.\n", + "#E7", + "LLM", + "Add functionality to the BooleanValidator plugin from #E6 to include options for default values when conversion fails", + "Plan: Implement options for default values when conversion fails.\n#E7 = LLM[Add functionality to the BooleanValidator plugin from #E6 to include options for default values when conversion fails]" + ], + [ + "Create the recordHook version of the Boolean Validator plugin.\n", + "#E8", + "LLM", + "Convert the BooleanValidator plugin from #E7 into a recordHook version, ensuring it adheres to the recordHook structure and requirements based on the information in #E1", + "Plan: Create the recordHook version of the Boolean Validator plugin.\n#E8 = LLM[Convert the BooleanValidator plugin from #E7 into a recordHook version, ensuring it adheres to the recordHook structure and requirements based on the information in #E1]" + ], + [ + "Create the external constraint version of the Boolean Validator plugin.\n", + "#E9", + "LLM", + "Convert the BooleanValidator plugin from #E7 into an external constraint version, ensuring it adheres to the external constraint structure and requirements based on the information in #E1", + "Plan: Create the external constraint version of the Boolean Validator plugin.\n#E9 = LLM[Convert the BooleanValidator plugin from #E7 into an external constraint version, ensuring it adheres to the external constraint structure and requirements based on the information in #E1]" + ], + [ + "Combine all components and create the final Boolean Validator Flatfile Listener plugin with both recordHook and external constraint versions.\n", + "#E10", + "LLM", + "Combine the BooleanValidator plugin implementations from #E7, #E8, and #E9 into a single file, exporting both recordHook and external constraint versions. Ensure all required functionality is included and the code is properly structured and commented", + "Plan: Combine all components and create the final Boolean Validator Flatfile Listener plugin with both recordHook and external constraint versions.\n#E10 = LLM[Combine the BooleanValidator plugin implementations from #E7, #E8, and #E9 into a single file, exporting both recordHook and external constraint versions. Ensure all required functionality is included and the code is properly structured and commented]" + ], + [ + "Validate the final Boolean Validator plugin code, check for unused imports, and ensure correct Event Topic usage.\n", + "#E11", + "PineconeAssistant", + "Validate the Boolean Validator plugin code from #E10, check for unused imports, ensure correct Event Topic usage, and verify that all required functionality is implemented correctly", + "Plan: Validate the final Boolean Validator plugin code, check for unused imports, and ensure correct Event Topic usage.\n#E11 = PineconeAssistant[Validate the Boolean Validator plugin code from #E10, check for unused imports, ensure correct Event Topic usage, and verify that all required functionality is implemented correctly]" + ], + [ + "Make any necessary corrections or improvements based on the validation results.\n", + "#E12", + "LLM", + "Review the validation results from #E11 and make any necessary corrections or improvements to the Boolean Validator plugin code", + "Plan: Make any necessary corrections or improvements based on the validation results.\n#E12 = LLM[Review the validation results from #E11 and make any necessary corrections or improvements to the Boolean Validator plugin code]" + ] + ], + "metrics": { + "tokens": { + "plan": 8662, + "state": 8209, + "total": 16871 + } + } +} \ No newline at end of file diff --git a/validate/boolean/package.json b/validate/boolean/package.json new file mode 100644 index 000000000..30a87182f --- /dev/null +++ b/validate/boolean/package.json @@ -0,0 +1,60 @@ +{ + "name": "@flatfile/plugin-validate-boolean", + "version": "0.0.0", + "description": "A Flatfile plugin for boolean validation with multi-language support", + "main": "./dist/index.cjs", + "module": "./dist/index.mjs", + "types": "./dist/index.d.ts", + "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" + }, + "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-plugins", + "category-validate" + ], + "author": "Flatfile, Inc.", + "license": "ISC", + "dependencies": { + "@flatfile/plugin-record-hook": "^1.7.0" + }, + "peerDependencies": { + "@flatfile/listener": "^1.0.5" + }, + "devDependencies": { + "@flatfile/rollup-config": "^0.1.1" + }, + "repository": { + "type": "git", + "url": "https://github.com/FlatFilers/flatfile-plugins.git", + "directory": "validate/boolean" + }, + "browserslist": [ + "> 0.5%", + "last 2 versions", + "not dead" + ] +} diff --git a/validate/boolean/rollup.config.mjs b/validate/boolean/rollup.config.mjs new file mode 100644 index 000000000..fafa813c6 --- /dev/null +++ b/validate/boolean/rollup.config.mjs @@ -0,0 +1,5 @@ +import { buildConfig } from '@flatfile/rollup-config' + +const config = buildConfig({}) + +export default config diff --git a/validate/boolean/src/index.ts b/validate/boolean/src/index.ts new file mode 100644 index 000000000..98c6b6d69 --- /dev/null +++ b/validate/boolean/src/index.ts @@ -0,0 +1 @@ +export * from './validate.boolean.plugin' diff --git a/validate/boolean/src/validate.boolean.plugin.e2e.spec.ts b/validate/boolean/src/validate.boolean.plugin.e2e.spec.ts new file mode 100644 index 000000000..65ed9a71c --- /dev/null +++ b/validate/boolean/src/validate.boolean.plugin.e2e.spec.ts @@ -0,0 +1,176 @@ +import { FlatfileClient } from '@flatfile/api' +import { + createRecords, + deleteSpace, + getRecords, + setupListener, + setupSimpleWorkbook, + setupSpace, +} from '@flatfile/utils-testing' +import { validateBoolean } from './validate.boolean.plugin' + +const api = new FlatfileClient() + +describe('validateBoolean e2e', () => { + const listener = setupListener() + + // Console spies + const logSpy = jest.spyOn(global.console, 'log') + const logErrorSpy = jest.spyOn(global.console, 'error') + + let spaceId: string + let sheetId: string + + beforeAll(async () => { + const space = await setupSpace() + spaceId = space.id + const workbook = await setupSimpleWorkbook(space.id, [ + { key: 'isActive', type: 'boolean' }, + { key: 'hasSubscription', type: 'boolean' }, + { key: 'agreeToTerms', type: 'boolean' }, + { key: 'optIn', type: 'boolean' }, + ]) + sheetId = workbook.sheets![0].id + }) + + afterAll(async () => { + await deleteSpace(spaceId) + }) + + afterEach(async () => { + listener.reset() + logSpy.mockReset() + logErrorSpy.mockReset() + const records = await getRecords(sheetId) + if (records.length > 0) { + const ids = records.map((record) => record.id) + await api.records.delete(sheetId, { ids }) + } + }) + + describe('validateBoolean()', () => { + it('validates strict boolean values', async () => { + listener.use( + validateBoolean({ + fields: ['isActive'], + validationType: 'strict' + }) + ) + + await createRecords(sheetId, [ + { isActive: true }, + { isActive: false }, + { isActive: 'true' }, + { isActive: 'false' }, + { isActive: 1 }, + { isActive: 0 }, + ]) + await listener.waitFor('commit:created') + + const records = await getRecords(sheetId) + + expect(records[0].values['isActive'].value).toBe(true) + expect(records[1].values['isActive'].value).toBe(false) + expect(records[2].values['isActive'].value).toBe(true) + expect(records[3].values['isActive'].value).toBe(false) + expect(records[4].values['isActive'].messages[0].message).toContain('Invalid boolean value') + expect(records[5].values['isActive'].messages[0].message).toContain('Invalid boolean value') + + }) + + it('validates truthy boolean values', async () => { + listener.use( + validateBoolean({ + fields: ['hasSubscription'], + validationType: 'truthy', + }) + ) + + await createRecords(sheetId, [ + { hasSubscription: true }, + { hasSubscription: false }, + { hasSubscription: 'true' }, + { hasSubscription: 'false' }, + { hasSubscription: 1 }, + { hasSubscription: 0 }, + { hasSubscription: 'yes' }, + { hasSubscription: 'no' }, + ]) + await listener.waitFor('commit:created') + + const records = await getRecords(sheetId) + + expect(records[0].values['hasSubscription'].value).toBeTruthy() + expect(records[1].values['hasSubscription'].value).toBeFalsy() + expect(records[2].values['hasSubscription'].value).toBeTruthy() + expect(records[3].values['hasSubscription'].value).toBeFalsy() + expect(records[4].values['hasSubscription'].value).toBeTruthy() + expect(records[5].values['hasSubscription'].value).toBeFalsy() + expect(records[6].values['hasSubscription'].value).toBeTruthy() + expect(records[7].values['hasSubscription'].value).toBeFalsy() + }) + + it('handles custom mapping', async () => { + listener.use( + validateBoolean({ + fields: ['agreeToTerms'], + validationType: 'truthy', + customMapping: { 'agreed': true, 'disagreed': false }, + }) + ) + + await createRecords(sheetId, [ + { agreeToTerms: 'agreed' }, + { agreeToTerms: 'disagreed' }, + { agreeToTerms: 'yes' }, + ]) + await listener.waitFor('commit:created') + + const records = await getRecords(sheetId) + + expect(records[0].values['agreeToTerms'].value).toBeTruthy() + expect(records[1].values['agreeToTerms'].value).toBeFalsy() + expect(records[2].values['agreeToTerms'].value).toBeTruthy() + }) + + it('handles null values', async () => { + listener.use( + validateBoolean({ + fields: ['optIn'], + validationType: 'truthy', + handleNull: 'false', + }) + ) + + await createRecords(sheetId, [ + { optIn: null }, + { optIn: undefined }, + { optIn: '' }, + ]) + await listener.waitFor('commit:created') + + const records = await getRecords(sheetId) + + expect(records[0].values['optIn'].value).toBe(false) + expect(records[1].values['optIn'].value).toBe(false) + expect(records[2].values['optIn'].value).toBe(false) + }) + + it('handles errors', async () => { + listener.use( + validateBoolean({ + fields: ['isActive'], + validationType: 'truthy', + }) + ) + + await createRecords(sheetId, [{ isActive: 'invalid' }]) + await listener.waitFor('commit:created') + + const records = await getRecords(sheetId) + + expect(records[0].values['isActive'].value).toBe('invalid') + expect(records[0].values['isActive'].messages[0].message).toContain('Invalid boolean value') + }) + }) +}) diff --git a/validate/boolean/src/validate.boolean.plugin.ts b/validate/boolean/src/validate.boolean.plugin.ts new file mode 100644 index 000000000..a3a179ba4 --- /dev/null +++ b/validate/boolean/src/validate.boolean.plugin.ts @@ -0,0 +1,139 @@ +import { type FlatfileListener } from '@flatfile/listener' +import { type FlatfileRecord, recordHook } from '@flatfile/plugin-record-hook' + +export interface BooleanValidatorConfig { + fields: string[] + validationType: 'strict' | 'truthy' + customMapping?: Record + caseSensitive?: boolean + handleNull?: 'error' | 'false' | 'true' | 'skip' + convertNonBoolean?: boolean + language?: string + customErrorMessages?: { + invalidBoolean?: string + invalidTruthy?: string + nullValue?: string + } + defaultValue?: boolean | 'skip' + sheetSlug?: string // New field to specify the sheet slug +} + +export const languageMappings: Record> = { + en: { yes: true, no: false, y: true, n: false }, + es: { sí: true, si: true, no: false, s: true, n: false }, + fr: { oui: true, non: false, o: true, n: false }, + de: { ja: true, nein: false, j: true, n: false }, +} + +export function handleNullValue( + record: FlatfileRecord, + field: string, + config: BooleanValidatorConfig +) { + switch (config.handleNull) { + case 'error': + record.addError( + field, + config.customErrorMessages?.nullValue || + 'Value cannot be null or undefined' + ) + break + case 'false': + record.set(field, false) + break + case 'true': + record.set(field, true) + break + case 'skip': + default: + // Do nothing, leave the field as is + break + } +} + +export function validateStrictBoolean( + record: FlatfileRecord, + field: string, + value: any, + config: BooleanValidatorConfig +) { + if (value === true || value === false) { + record.set(field, value) + } else if (config.convertNonBoolean) { + record.set(field, Boolean(value)) + } else { + handleInvalidValue(record, field, config) + } +} + +export function validateTruthyBoolean( + record: FlatfileRecord, + field: string, + value: any, + config: BooleanValidatorConfig +) { + const defaultMapping = config.language + ? languageMappings[config.language] + : languageMappings.en + const mapping = config.customMapping || defaultMapping + + let normalizedValue = value + if (typeof value === 'string' && !config.caseSensitive) { + normalizedValue = value.toLowerCase() + } + + if (normalizedValue === true || normalizedValue === false) { + record.set(field, normalizedValue) + } else if (mapping.hasOwnProperty(normalizedValue)) { + record.set(field, mapping[normalizedValue]) + } else if (typeof normalizedValue === 'number') { + record.set(field, Boolean(normalizedValue)) + } else if (config.convertNonBoolean) { + record.set(field, Boolean(value)) + } else { + handleInvalidValue(record, field, config) + } +} +export function handleInvalidValue( + record: FlatfileRecord, + field: string, + config: BooleanValidatorConfig +) { + if (config.defaultValue === undefined || config.defaultValue === 'skip') { + record.addError( + field, + config.customErrorMessages?.invalidBoolean || 'Invalid boolean value' + ) + } else { + record.set(field, config.defaultValue) + record.addInfo( + field, + `Invalid value converted to default: ${config.defaultValue}` + ) + } +} + +// Updated RecordHook version +export const validateBoolean = (config: BooleanValidatorConfig) => { + return (listener: FlatfileListener) => { + listener.use( + recordHook(config.sheetSlug || '**', async (record: FlatfileRecord) => { + config.fields.forEach((field) => { + const value = record.get(field) + + if (value === null || value === undefined) { + handleNullValue(record, field, config) + } else if (config.validationType === 'strict') { + validateStrictBoolean(record, field, value, config) + } else { + validateTruthyBoolean(record, field, value, config) + } + }) + + return record + }) + ) + } +} + +export default validateBoolean