diff --git a/Changelog.md b/Changelog.md index 4906955b6..cb8ba8c82 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,5 +1,18 @@ # Changelog +## 20231128 + +- Data synchronization article added + +## 20231128 – Fixes and minor updates + +- Fixed to consistent title format (capital letters) +- Added excerpts +- Added green callouts in the Integration blueprint section +- Fixed an example email address in First Steps in Voucherify article +- Fixed image in the Integration Overview article +- Added link to the new article – Data Synchronization – in the Data Model article + ## 20231123 - Campaigns API **Added schemas** diff --git a/docs/guides/campaign_recipes/Qualifications.md b/docs/guides/campaign_recipes/Qualifications.md index edb314ae1..202d47543 100644 --- a/docs/guides/campaign_recipes/Qualifications.md +++ b/docs/guides/campaign_recipes/Qualifications.md @@ -1,5 +1,5 @@ --- -title: Qualification - Checking eligibility +title: Qualification – Checking Eligibility excerpt: null categorySlug: campaigns-recipes slug: checking-eligibility diff --git a/docs/guides/development/Metadata-Mapping.md b/docs/guides/development/Metadata-Mapping.md index 7539a0b8d..157fb6fc2 100644 --- a/docs/guides/development/Metadata-Mapping.md +++ b/docs/guides/development/Metadata-Mapping.md @@ -1,6 +1,6 @@ --- title: Metadata Mapping -excerpt: Metadata mapping +excerpt: Custom attributes (metadata) and their use in Voucherify categorySlug: development slug: metadata-mapping type: basic @@ -8,7 +8,7 @@ hidden: false order: 50 --- -## Metadata (custom attributes) +## Custom attributes (metadata) Custom attributes can be added to your project as metadata. A metadata attribute is a set of key/value pairs that you can use to customize your campaigns, vouchers, customers, SKUs, products, redemptions, publications, loyalty tiers, promotion tiers, and orders. diff --git a/docs/guides/development/Quickstart.md b/docs/guides/development/Quickstart.md index a8ed6075c..71d234693 100644 --- a/docs/guides/development/Quickstart.md +++ b/docs/guides/development/Quickstart.md @@ -1,5 +1,5 @@ --- -title: First steps in Voucherify +title: First Steps in Voucherify excerpt: Get started with Voucherify API and dashboard by redeeming your first coupon code. categorySlug: development slug: quickstart @@ -128,7 +128,7 @@ Voucherify should reply with the [redemption](ref:get-redemption) details as in "customer": { "id": "cust_ANjd4MFsUPXDoHeoCQXmqgCJ", "name": "Jack Jack", - "email": "maciej.krzak+Jack@voucherify.io", + "email": "jack.jack@somedomain.com", "source_id": "16102023", "metadata": { "metadata_key": "metadata_value", diff --git a/docs/guides/development/Test-Mode-Sandbox.md b/docs/guides/development/Test-Mode-Sandbox.md index c90ddc056..e02c9b1d0 100644 --- a/docs/guides/development/Test-Mode-Sandbox.md +++ b/docs/guides/development/Test-Mode-Sandbox.md @@ -1,5 +1,5 @@ --- -title: Test mode (Sandbox) +title: Test Mode (Sandbox) excerpt: Run integration tests of your promotions before going to production categorySlug: development slug: testing diff --git a/docs/guides/development/data-synchronization.md b/docs/guides/development/data-synchronization.md new file mode 100644 index 000000000..f9712a07e --- /dev/null +++ b/docs/guides/development/data-synchronization.md @@ -0,0 +1,160 @@ +--- +title: Data synchronization +excerpt: Learn what kind of data you can synchronize with Voucherify and how you can do this +categorySlug: development +slug: data-synchronization +type: basic +hidden: false +order: 60 +--- + +In Voucherify, you can synchronize the following data: +- [Customers](#customer-synchronization) +- [Products](#product-synchronization) +- [Orders](#order-synchronization) + + +When you have [created your Voucherify account](doc:getting-started), you can import your existing data and then synchronize it for future use in campaigns. + +> πŸ“˜ **Goals**: +> +> - Learn why data synchronization is important +> - Learn how to synchronize your data through data import and updates + +> πŸ‘ **Outcome**: +> +> Your data is synchronized with Voucherify. + +## Customer synchronization + +Voucherify can store and act on collected customer data. The most common customer data operation is to create customer segments based on customer attributes. Customer segments can be used to understand customer behavior, build promotion limits, or trigger automatic incentive delivery. + +Voucherify uses the [customer object](ref:customer-object) for [validation](ref:validation-object), [redemption](ref:redemption-object), and [distribution](https://support.voucherify.io/article/19-how-does-the-distribution-manager-work "How does the distribution manager work?") purposes. + +The customer data does not have to be stored in Voucherify before a validation or redemption request is made. Voucherify can use the customer data from the request to create a new customer with a unique `id` field. + +However, the customer data must exist in Voucherify before a distribution is made. Also, distribution can be made on the basis of the customer's attributes and sent out to customers from a given segment. If the customer data is not up to date, the distribution messages sent manually can reach the wrong audience. + + + +> 🚧 Customer data synchronization and `source_id` +> +> If your data is to be synchronized based on the `source_id` of the customer, you need to import the `source_id` when the customer is uploaded for the first time. You will not be able to update `source_id` later on. + +### Customer synchronization options + + + +#### Upserting customers + +If the customer does not exist in Voucherify, they are created automatically in a request that includes the customer object. If the customer exists, their data will be upserted. In this case, the response also includes the `updated_at` object. + +The following actions upsert customer data: +- [Order creation](ref:create-order) +- [Stackable discount redemption](ref:redeem-stacked-discounts) +- [Stackable discount redemption (client side)](ref:redeem-stacked-discounts-client-side) +- [Track custom event](ref:track-custom-event) +- [Voucher publication](ref:create-publication) + +#### Importing customers + +You can import your customer database to Voucherify with a CSV file. If you use a CRM system, use its unique ID as a `source_id` field. + +Read our [customer import guide](https://support.voucherify.io/article/67-how-to-import-my-customers#csv-import "How to import my customers?") to learn more. + +#### Creating and updating customers with the API + +If you want to keep your customers database up to date with Voucherify, create a customer in Voucherify every time a new user is added to your database. Use the [create customer](ref:create-customer) API endpoint to create customer data. + +To update customer data, use [update customer](ref:update-customer) API endpoint. The [create customer](ref:create-customer) API endpoint can be also used to update customer data. + +#### Synchronizing with connectors + +Customer data can be upserted with integrated platforms that support outbound traffic: +- [Bloomreach Engagement](https://support.voucherify.io/article/613-bloomreach-engagement-integration "Voucherify-Bloomreach Engagement integration article") +- [Braze](https://support.voucherify.io/article/588-braze-integration "Voucherify-Braze integration article") +- [mParticle](https://support.voucherify.io/article/590-mparticle "Voucherify-mParticle integration article") +- [Salesforce](https://support.voucherify.io/article/140-salesforce "Voucherify-Salesforce integration article") +- [Segment](https://support.voucherify.io/article/272-segment "Voucherify-Segment integration article") +- [Zapier](https://support.voucherify.io/article/269-zapier "Voucherify-Zapier integration article") + +### Customer API endpoints + +Go to the API reference to see the [customer endpoints](ref:customer-object) that can be used to synchronize customer data. + +## Product synchronization + +Products can be stored in Voucherify but it is not required. The product validation takes place during the following actions: +- Qualification +- Validation +- Redemption + + + +The product validation is based on the API payload and the data stored in Voucherify's product inventory. + +However, if a product does not exist in Voucherify, the validation will use the product data included in the payload. Even if the product does exist in Voucherify, the payload data is used instead of those stored in the system. This method can be used to avoid frequent API calls to keep the product inventory up to date. + +> 🚧 Product data in the request +> +> Unlike customer data, product data provided in the request payload alone do not create a new product in Voucherify. If you want to store products in Voucherify, they need to be created manually in the dashboard or imported with a CSV file. +> +> Also, even though Voucherify uses the payload data instead of the data stored in the system, the product data is not upserted through requests. Products can be updated through product edit function or by importing a CSV file. +> +> To enable product updates through the payload, use the [override attribute](#overriding-product-data). + +The products in Voucherify can be grouped into static or dynamic collections based on their parameters. Collections can be used in validation rules to model product-specific limits and loyalty-earning rules. + +### Product synchronization options + +#### Importing products + +You can [import your product database](https://support.voucherify.io/article/515-products#import-products-skus "Import Products and SKUs by CSV") to Voucherify with a CSV file. This method can be also used to update the names of existing products. + +#### Creating and updating products with the API + +You can create and update your products with the following API endpoints: +- [Create product](ref:create-product) +- [Update product](ref:update-product) + +#### Overriding product data + +In a request payload, you can add an attribute `override` with value `true` to the product object. + +The `override` attribute is used to enable storing the product data in Voucherify. If the product does not exist, it will be created with a `source_id`. If the product does exist, the provided values for the name, price, and metadata will replace those already stored in Voucherify. + +## Order synchronization + +Orders are synchronized with Voucherify automatically during redemption. They can be viewed through the [Orders view](https://support.voucherify.io/article/263-how-can-i-track-customer-orders "How can I track customer orders?") in the dashboard. + +If you want to synchronize the orders that have not been included in any redemption, you need to use the [import order](#import-orders) method. + +### Order synchronization options + +#### Create order + +The [create order](ref:create-order) endpoint creates an [order object](ref:order-object) and triggers an order creation event. + +The endpoint can be also used to upsert order data if the order `id` or `source_id` are provided. + +#### Update order + +The [update order](ref:update-order) endpoint updates the specified order with the values of the parameters in the payload. + +#### Import orders + +The [import orders](ref:import-orders) endpoint is used to import only historical orders into Voucherify. For on-going synchronization, the [create order](ref:create-order) and [update order](ref:update-order) endpoints should be used. Importing orders in bulk does not trigger distributions or earning rules. + + \ No newline at end of file diff --git a/docs/guides/development/implementation-checklist.md b/docs/guides/development/implementation-checklist.md index 5c6db38e1..a81852f41 100644 --- a/docs/guides/development/implementation-checklist.md +++ b/docs/guides/development/implementation-checklist.md @@ -1,5 +1,5 @@ --- -title: Development checklist +title: Development Checklist excerpt: Get the documentation, sample code, and developer tools β€” everything you need to build digital promotions faster categorySlug: development slug: welcome diff --git a/docs/guides/development/integration-connectors.md b/docs/guides/development/integration-connectors.md index 55d63c25e..3e084e9bd 100644 --- a/docs/guides/development/integration-connectors.md +++ b/docs/guides/development/integration-connectors.md @@ -1,5 +1,5 @@ --- -title: Integration and connectors +title: Integration And Connectors excerpt: categorySlug: development slug: integration-connectors diff --git a/docs/guides/getting_started/Introduction.md b/docs/guides/getting_started/Introduction.md index 88451a190..2f6eb39b6 100644 --- a/docs/guides/getting_started/Introduction.md +++ b/docs/guides/getting_started/Introduction.md @@ -1,6 +1,6 @@ --- title: Introduction -excerpt: Welcome to [Voucherify's](https://www.voucherify.io) Developer Hub. +excerpt: Welcome to [Voucherify's](https://www.voucherify.io) developer hub. categorySlug: getting-started slug: introduction type: basic @@ -12,13 +12,14 @@ order: 1 The documentation is divided into four main categories. -1. **[Getting started](doc:welcome-to-voucherify)** - Learn more about Voucherify and what’s possible with our platform. Take your first steps to set up your account and start testing. Recommended for everyone. +1. **[Getting started](doc:welcome-to-voucherify)** – Learn more about Voucherify and what’s possible with our platform. Take your first steps to set up your account and start testing. Recommended for everyone. + +2. **[Integration Blueprint](doc:integration-overview)** – Discover how to seamlessly plan your integration and learn how to build advanced promotion limits to personalize your campaigns and prevent fraud. Recommended for architects and readers who want to know how to plan out the integration. -2. **[Integration Blueprint](doc:integration-overview)** - Discover how to seamlessly plan your integration and learn how to build advanced promotion limits to personalize your campaigns and prevent fraud. Recommended for architects and readers who want to know how to plan out the integration. 3. **[Development](doc:welcome)** - Dive into detailed development options, including example data mapping, processes, and more. Recommended for developers and integrators. -4. **[API Reference](doc:api-reference)** - Explore endpoints and attributes with request/response examples and error messages. +4. **[API Reference](doc:api-reference)** – Explore endpoints and attributes with request/response examples and error messages.     diff --git a/docs/guides/getting_started/project-setup-guide.md b/docs/guides/getting_started/project-setup-guide.md index 0f9dc5763..f88370de8 100644 --- a/docs/guides/getting_started/project-setup-guide.md +++ b/docs/guides/getting_started/project-setup-guide.md @@ -1,5 +1,5 @@ --- -title: Getting started +title: Getting Started excerpt: Set up your Voucherify project categorySlug: getting-started slug: getting-started diff --git a/docs/guides/integration_blueprint/Campaign-reporting.md b/docs/guides/integration_blueprint/Campaign-reporting.md index 72aa243ad..c439d138e 100644 --- a/docs/guides/integration_blueprint/Campaign-reporting.md +++ b/docs/guides/integration_blueprint/Campaign-reporting.md @@ -1,6 +1,6 @@ --- -title: Campaign reporting -excerpt: +title: Campaign Reporting +excerpt: Gain insights into campaigns with reports categorySlug: integration-blueprint slug: campaign-reporting type: basic @@ -8,17 +8,15 @@ hidden: false order: 12 --- -## Campaign reporting - > πŸ“˜ **Goals** > > * Identify key metrics of promotion effectiveness. > * Learn Voucherify API and Dashboard reporting capabilities. -**Outcome**: - -Connect Voucherify to external BI tools or prepare reports using built-in campaign analytics. +> πŸ‘ **Outcome**: +> +> Connect Voucherify to external BI tools or prepare reports using built-in campaign analytics. --- diff --git a/docs/guides/integration_blueprint/Data-Model.md b/docs/guides/integration_blueprint/Data-Model.md index 841be1461..16af12e7e 100644 --- a/docs/guides/integration_blueprint/Data-Model.md +++ b/docs/guides/integration_blueprint/Data-Model.md @@ -8,16 +8,14 @@ hidden: false order: 8 --- -## Data model - > πŸ“˜ **Goals** > > * Learn more about metadata. > * Choose which attributes and events you would like to share with Voucherify. -**Outcome**: - -Data model PoC that is based on your use cases and business model. +> πŸ‘ **Outcome**: +> +> Data model PoC that is based on your use cases and business model. --- @@ -41,11 +39,11 @@ For the initial data migration, use asynchronous bulk API import endpoints or up > πŸ“˜ Data synchronization with Voucherify > -> Voucherify gives you complete control over **how and when to sync data** needed for incentive validation and redemption. You can sync the data before the final stage of the checkout process or send us relevant customer, product, order, and other data right in the validation and redemption requests on the fly. +> Voucherify gives you complete control over **how and when to [synchronize data](doc:data-synchronization)** needed for incentive validation and redemption. You can sync the data before the final stage of the checkout process or send us relevant customer, product, order, and other data right in the validation and redemption requests on the fly. ### Customer story – TIER Mobility -TIER Mobility uses free unlocks and free minutes as incentives. The custom attributes +TIER Mobility uses free unlocks and free minutes as incentives. The custom attributes (metadata) let TIER attach minutes and unlocks to a given customer for a predefined period of time. [Discover the full story](https://voucherify.io/ebooks/tier-mobility-case-study "Tier mobility case study") diff --git a/docs/guides/integration_blueprint/Data-Volume-Estimation.md b/docs/guides/integration_blueprint/Data-Volume-Estimation.md index 3723f690f..b4b1a83b9 100644 --- a/docs/guides/integration_blueprint/Data-Volume-Estimation.md +++ b/docs/guides/integration_blueprint/Data-Volume-Estimation.md @@ -1,6 +1,6 @@ --- title: Data Volume Estimation -excerpt: +excerpt: Estimate data volume used with Voucherify categorySlug: integration-blueprint slug: data-volume-estimation type: basic @@ -8,17 +8,15 @@ hidden: false order: 15 --- -## Data volume estimation - > πŸ“˜ **Goals** > > * Understand what API endpoints will be used to map your scenarios. > * Estimate how many customers will be included in promotions and how often their data should be synced with Voucherify. > * Think of what it takes to migrate promotions from your current system. -**Outcome**: - -API usage forecast, including monthly and hourly API calls number and a Voucheify deployment plan. +> πŸ‘ **Outcome**: +> +> API usage forecast, including monthly and hourly API calls number and a Voucheify deployment plan. --- diff --git a/docs/guides/integration_blueprint/Distributions.md b/docs/guides/integration_blueprint/Distributions.md index 839588d9f..5492c49d3 100644 --- a/docs/guides/integration_blueprint/Distributions.md +++ b/docs/guides/integration_blueprint/Distributions.md @@ -1,6 +1,6 @@ --- title: Distributions -excerpt: +excerpt: Distribute incentives through preferred channels categorySlug: integration-blueprint slug: distributions type: basic @@ -8,17 +8,15 @@ hidden: false order: 11 --- -## Distributions - > πŸ“˜ **Goals** > > * Identify which channels should be used for distribution. > * Learn how Voucherify publication works. > * Discover Voucherify integrations for messaging. -**Outcome**: - -Connect Voucherify with your preferred communication channels. +> πŸ‘ **Outcome**: +> +> Connect Voucherify with your preferred communication channels. --- @@ -35,7 +33,7 @@ Both push and pull modes can be used with third party platforms: - [Iterable](https://support.voucherify.io/article/594-iterable-integration "Voucherify and Iterable integration article") - Airship - [ActiveCampaign](https://support.voucherify.io/article/165-activecampaign "Voucherify and ActiveCampaign integration article") -- Batch +- [Batch](https://support.voucherify.io/article/614-batch-integration "Voucherify and Batch integration article") diff --git a/docs/guides/integration_blueprint/Multi-brand management & internationalization.md b/docs/guides/integration_blueprint/Multi-brand management & internationalization.md index 744dbf026..b17479002 100644 --- a/docs/guides/integration_blueprint/Multi-brand management & internationalization.md +++ b/docs/guides/integration_blueprint/Multi-brand management & internationalization.md @@ -1,6 +1,6 @@ --- -title: Multi-brand management & internationalization -excerpt: +title: Multi-Brand Management And Internationalization +excerpt: Campaign management across many brands and countries categorySlug: integration-blueprint slug: brand-management type: basic @@ -8,23 +8,21 @@ hidden: false order: 13 --- -## Multi-brand management & internationalization - > πŸ“˜ **Goals** > > * Understand how Voucherify supports multiple brands and locations through projects. -**Outcome**: - -Ability to set up separate projects for different locations, currencies, brands, or development phases. +> πŸ‘ **Outcome**: +> +> Ability to set up separate projects for different locations, currencies, brands, or development phases. --- Voucherify supports **different approaches to campaign management**. You can use multiple projects to map different contexts or a shared project by using categories, labels, promo code prefixes, or custom fields to differentiate campaigns. Learn how projects can be used to isolate business context and its impact on maintenance and reporting: -* Currency & timezone. +* Currency and timezone. * Users and user roles. -* Email settings & marketing consents. +* Email settings and marketing consents. * Metadata schema. * Categories. * API keys, usage & webhooks. @@ -32,4 +30,4 @@ Voucherify supports **different approaches to campaign management**. You can use > πŸ“˜ Voucherify Management API > > Voucherify offers a Management API which allows large teams to manage projects via API, for instance, to set up new projects -under a specific configuration fully programmatically. --!> +under a specific configuration fully programmatically. --!> \ No newline at end of file diff --git a/docs/guides/integration_blueprint/Security.md b/docs/guides/integration_blueprint/Security.md index 3e8a74810..a39e25c23 100644 --- a/docs/guides/integration_blueprint/Security.md +++ b/docs/guides/integration_blueprint/Security.md @@ -1,6 +1,6 @@ --- title: Security -excerpt: +excerpt: Voucherify data security categorySlug: integration-blueprint slug: security-1 type: basic @@ -8,17 +8,15 @@ hidden: false order: 16 --- -## Security - > πŸ“˜ **Goals** > > * Learn how Voucherify guarantees campaign security. > * Identify PII data to be shared with Voucherify. > * Analyze your distribution processes in relation to GDPR and Data Privacy laws. -**Outcome**: - -Data security assessment of integration. +> πŸ‘ **Outcome**: +> +> Data security assessment of integration. --- diff --git a/docs/guides/integration_blueprint/Team-management.md b/docs/guides/integration_blueprint/Team-management.md index 639fbd853..426a22811 100644 --- a/docs/guides/integration_blueprint/Team-management.md +++ b/docs/guides/integration_blueprint/Team-management.md @@ -1,6 +1,6 @@ --- title: Team Management -excerpt: +excerpt: Managing your team in Voucherify categorySlug: integration-blueprint slug: team-management type: basic @@ -8,17 +8,15 @@ hidden: false order: 13 --- -## Team management - > πŸ“˜ **Goals** > > * Identify how you want to structure teams working with Voucherify. > * Think of roles and permissions necessary to manage campaigns. > * Learn more about user management and approval workflows. -**Outcome**: - -Invite team members with specific roles and set up approval workflows to keep your campaigns safe. +> πŸ‘ **Outcome**: +> +> Invite team members with specific roles and set up approval workflows to keep your campaigns safe. --- diff --git a/docs/guides/integration_blueprint/campaign-limits.md b/docs/guides/integration_blueprint/campaign-limits.md index 9b621d77b..4560f10ef 100644 --- a/docs/guides/integration_blueprint/campaign-limits.md +++ b/docs/guides/integration_blueprint/campaign-limits.md @@ -1,5 +1,5 @@ --- -title: Campaign limits +title: Campaign Limits excerpt: categorySlug: integration-blueprint slug: campaign-limits @@ -14,9 +14,9 @@ order: 10 > - Learn the built-in validation rules of Voucherify. > - Discover how to extend your campaigns with custom limits built on top of business-specific restrictions. -**Outcome**: - -Ability to build advanced promotion limits to personalize your campaigns and prevent fraud. +> πŸ‘ **Outcome**: +> +> Ability to build advanced promotion limits to personalize your campaigns and prevent fraud. --- diff --git a/docs/guides/integration_blueprint/integration-overview.md b/docs/guides/integration_blueprint/integration-overview.md index 7b5ebb15c..9b2129c3b 100644 --- a/docs/guides/integration_blueprint/integration-overview.md +++ b/docs/guides/integration_blueprint/integration-overview.md @@ -1,6 +1,6 @@ --- -title: Integration overview -excerpt: +title: Integration Overview +excerpt: What does the integration entail? categorySlug: integration-blueprint slug: integration-overview type: basic @@ -14,7 +14,7 @@ Voucherify offers **a business-agnostic API to model any promotion case**. You c A pivotal factor for evaluating the total cost of integration is to understand how Voucherify can be connected to your e-commerce and marketing infrastructure that runs your business. There are three angles to consider. -![Voucherify as an ecommerce engine](https://files.readme.io/fce4ab0-guides_integration_blueprint_how_voucherify_helps-01.png "Voucherify as an ecommerce engine") +![How Voucherify works with your system and our rules engine](https://files.readme.io/494bc1a-guides_getting_started_welcome_to_voucherify_voucherify_workflow_scheme_01.png "How Voucherify works with your system and our rules engine") ## Ecommerce engine diff --git a/docs/guides/integration_blueprint/modeling-voucherify-integration.md b/docs/guides/integration_blueprint/modeling-voucherify-integration.md index a8cb88e0b..dee1ee44c 100644 --- a/docs/guides/integration_blueprint/modeling-voucherify-integration.md +++ b/docs/guides/integration_blueprint/modeling-voucherify-integration.md @@ -1,6 +1,6 @@ --- -title: Modeling Voucherify integration -excerpt: +title: Modeling Voucherify Integration +excerpt: Elements in Voucherify integrations categorySlug: integration-blueprint slug: modeling-voucherify-integration type: basic @@ -14,7 +14,6 @@ As a developer-first tool, Voucherify exposes several integration paths: - **REST API** – a rich set of **granular REST APIs** that help you deliver personalized incentives to any channel, device, and commerce solution while keeping your data in secure, highly available environments in a multi-tenant or dedicated cluster near you. - [Check our interactive API Reference](https://docs.voucherify.io/reference/introduction-1 "Voucherify interactive API reference") - **Webhooks** – allow Voucherify to send instant updates to your app or URL endpoints when specific events occur. Rather than constantly checking endpoints for new data, you can **set up webhooks to receive notifications** about critical campaign-related events. @@ -27,8 +26,6 @@ As a developer-first tool, Voucherify exposes several integration paths: - **CEP integrations** – Voucherify provides ready connectors with Customer Engagement Platforms, such as [Bloomreach Engagement](https://support.voucherify.io/article/613-bloomreach-engagement-integration "Bloomreach Engagement Integration with Voucherify"), [Braze](https://www.voucherify.io/integrations/braze "Omnichannel incentives to grow customer engagement"), [Klaviyo](https://www.voucherify.io/integrations/klaviyo "Maximize customer engagement"), or [MoEngage](https://www.voucherify.io/integrations/moengage "Engage customers with cross-channel promotions"). - - - **Accelerators** – to accelerate time to value, Voucherify offers pre-built accelerators with** the most popular commerce and marketing tools**, such as [commercetools](https://www.voucherify.io/integrations/commercetools "Next-gen promotions for modern commerce"), [BigCommerce](https://www.voucherify.io/integrations/bigcommerce "Supercharge your online store with personalized promotions"), or [Braze](https://www.voucherify.io/integrations/braze "Omnichannel incentives to grow customer engagement"). [Discover our Technology Partners](https://www.voucherify.io/integrations "Supercharge promotions with powerful Voucherify integrations") diff --git a/docs/guides/integration_blueprint/scenario-discovery.md b/docs/guides/integration_blueprint/scenario-discovery.md index fcdca207c..9634a95d2 100644 --- a/docs/guides/integration_blueprint/scenario-discovery.md +++ b/docs/guides/integration_blueprint/scenario-discovery.md @@ -1,5 +1,5 @@ --- -title: Scenario discovery +title: Scenario Discovery excerpt: Discover various Voucherify scenarios categorySlug: integration-blueprint slug: scenario-discovery @@ -8,17 +8,15 @@ hidden: false order: 4 --- -## Scenarios discovery - > πŸ“˜ **Goals**: > > - Identify a campaign for your pilot project. > - Learn key capabilities for each campaign type. > - Uncover roadblocks and 3rd-party integrations. -**Outcome**: - -Creation of an initial POC for Voucherify integration. +> πŸ‘ **Outcome**: +> +> Creation of an initial POC for Voucherify integration. --- diff --git a/package-lock.json b/package-lock.json index fde0aac8d..4b5beae7a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "axios": "^1.5.1", "colors": "^1.4.0", "dotenv": "^16.3.1", + "lodash": "^4.17.21", "markdown-it": "^13.0.2", "minimist": "^1.2.8", "node-fetch": "^2.7.0", @@ -17,9 +18,23 @@ "yup": "^1.1.1" }, "devDependencies": { + "@openapitools/openapi-generator-cli": "^2.7.0", + "@types/lodash": "^4.14.202", "@types/node": "^20.7.0" } }, + "node_modules/@babel/runtime": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.4.tgz", + "integrity": "sha512-2Yv65nlWnWlSpe3fXEyX5i7fx5kIKo4Qbcj+hMO0odwaneFjfXw5fdum+4yL20O0QiaHpia0cYQ9xpNMqrBwHg==", + "dev": true, + "dependencies": { + "regenerator-runtime": "^0.14.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@cspotcode/source-map-support": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", @@ -53,6 +68,175 @@ "@jridgewell/sourcemap-codec": "^1.4.10" } }, + "node_modules/@lukeed/csprng": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@lukeed/csprng/-/csprng-1.1.0.tgz", + "integrity": "sha512-Z7C/xXCiGWsg0KuKsHTKJxbWhpI3Vs5GwLfOean7MGyVFGqdRgBbAjOCh6u4bbjPc/8MJ2pZmK/0DLdCbivLDA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@nestjs/axios": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@nestjs/axios/-/axios-0.1.0.tgz", + "integrity": "sha512-b2TT2X6BFbnNoeteiaxCIiHaFcSbVW+S5yygYqiIq5i6H77yIU3IVuLdpQkHq8/EqOWFwMopLN8jdkUT71Am9w==", + "dev": true, + "dependencies": { + "axios": "0.27.2" + }, + "peerDependencies": { + "@nestjs/common": "^7.0.0 || ^8.0.0 || ^9.0.0", + "reflect-metadata": "^0.1.12", + "rxjs": "^6.0.0 || ^7.0.0" + } + }, + "node_modules/@nestjs/axios/node_modules/axios": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz", + "integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==", + "dev": true, + "dependencies": { + "follow-redirects": "^1.14.9", + "form-data": "^4.0.0" + } + }, + "node_modules/@nestjs/common": { + "version": "9.3.11", + "resolved": "https://registry.npmjs.org/@nestjs/common/-/common-9.3.11.tgz", + "integrity": "sha512-IFZ2G/5UKWC2Uo7tJ4SxGed2+aiA+sJyWeWsGTogKVDhq90oxVBToh+uCDeI31HNUpqYGoWmkletfty42zUd8A==", + "dev": true, + "dependencies": { + "iterare": "1.2.1", + "tslib": "2.5.0", + "uid": "2.0.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nest" + }, + "peerDependencies": { + "cache-manager": "<=5", + "class-transformer": "*", + "class-validator": "*", + "reflect-metadata": "^0.1.12", + "rxjs": "^7.1.0" + }, + "peerDependenciesMeta": { + "cache-manager": { + "optional": true + }, + "class-transformer": { + "optional": true + }, + "class-validator": { + "optional": true + } + } + }, + "node_modules/@nestjs/common/node_modules/tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "dev": true + }, + "node_modules/@nestjs/core": { + "version": "9.3.11", + "resolved": "https://registry.npmjs.org/@nestjs/core/-/core-9.3.11.tgz", + "integrity": "sha512-CI27a2JFd5rvvbgkalWqsiwQNhcP4EAG5BUK8usjp29wVp1kx30ghfBT8FLqIgmkRVo65A0IcEnWsxeXMntkxQ==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "@nuxtjs/opencollective": "0.3.2", + "fast-safe-stringify": "2.1.1", + "iterare": "1.2.1", + "path-to-regexp": "3.2.0", + "tslib": "2.5.0", + "uid": "2.0.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nest" + }, + "peerDependencies": { + "@nestjs/common": "^9.0.0", + "@nestjs/microservices": "^9.0.0", + "@nestjs/platform-express": "^9.0.0", + "@nestjs/websockets": "^9.0.0", + "reflect-metadata": "^0.1.12", + "rxjs": "^7.1.0" + }, + "peerDependenciesMeta": { + "@nestjs/microservices": { + "optional": true + }, + "@nestjs/platform-express": { + "optional": true + }, + "@nestjs/websockets": { + "optional": true + } + } + }, + "node_modules/@nestjs/core/node_modules/tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "dev": true + }, + "node_modules/@nuxtjs/opencollective": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@nuxtjs/opencollective/-/opencollective-0.3.2.tgz", + "integrity": "sha512-um0xL3fO7Mf4fDxcqx9KryrB7zgRM5JSlvGN5AGkP6JLM5XEKyjeAiPbNxdXVXQ16isuAhYpvP88NgL2BGd6aA==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "consola": "^2.15.0", + "node-fetch": "^2.6.1" + }, + "bin": { + "opencollective": "bin/opencollective.js" + }, + "engines": { + "node": ">=8.0.0", + "npm": ">=5.0.0" + } + }, + "node_modules/@openapitools/openapi-generator-cli": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/@openapitools/openapi-generator-cli/-/openapi-generator-cli-2.7.0.tgz", + "integrity": "sha512-ieEpHTA/KsDz7ANw03lLPYyjdedDEXYEyYoGBRWdduqXWSX65CJtttjqa8ZaB1mNmIjMtchUHwAYQmTLVQ8HYg==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "@nestjs/axios": "0.1.0", + "@nestjs/common": "9.3.11", + "@nestjs/core": "9.3.11", + "@nuxtjs/opencollective": "0.3.2", + "chalk": "4.1.2", + "commander": "8.3.0", + "compare-versions": "4.1.4", + "concurrently": "6.5.1", + "console.table": "0.10.0", + "fs-extra": "10.1.0", + "glob": "7.1.6", + "inquirer": "8.2.5", + "lodash": "4.17.21", + "reflect-metadata": "0.1.13", + "rxjs": "7.8.0", + "tslib": "2.0.3" + }, + "bin": { + "openapi-generator-cli": "main.js" + }, + "engines": { + "node": ">=10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/openapi_generator" + } + }, "node_modules/@tsconfig/node10": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", @@ -73,6 +257,12 @@ "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==" }, + "node_modules/@types/lodash": { + "version": "4.14.202", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.202.tgz", + "integrity": "sha512-OvlIYQK9tNneDlS0VN54LLd5uiPCBOp7gS5Z0f1mjoJYBrtStzgmJBxONW3U6OZqdtNzZPmn9BS/7WI7BFFcFQ==", + "dev": true + }, "node_modules/@types/node": { "version": "20.7.0", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.7.0.tgz", @@ -97,6 +287,57 @@ "node": ">=0.4.0" } }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-escapes/node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, "node_modules/arg": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", @@ -122,6 +363,170 @@ "proxy-from-env": "^1.1.0" } }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dev": true, + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", + "dev": true + }, + "node_modules/cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, + "dependencies": { + "restore-cursor": "^3.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-spinners": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", + "dev": true, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-width": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", + "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", + "dev": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", + "dev": true, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, "node_modules/colors": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", @@ -141,11 +546,133 @@ "node": ">= 0.8" } }, + "node_modules/commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", + "dev": true, + "engines": { + "node": ">= 12" + } + }, + "node_modules/compare-versions": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-4.1.4.tgz", + "integrity": "sha512-FemMreK9xNyL8gQevsdRMrvO4lFCkQP7qbuktn1q8ndcNk1+0mz7lgE7b/sNvbhVgY4w6tMN1FDp6aADjqw2rw==", + "dev": true + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "node_modules/concurrently": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-6.5.1.tgz", + "integrity": "sha512-FlSwNpGjWQfRwPLXvJ/OgysbBxPkWpiVjy1042b0U7on7S7qwwMIILRj7WTN1mTgqa582bG6NFuScOoh6Zgdag==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "date-fns": "^2.16.1", + "lodash": "^4.17.21", + "rxjs": "^6.6.3", + "spawn-command": "^0.0.2-1", + "supports-color": "^8.1.0", + "tree-kill": "^1.2.2", + "yargs": "^16.2.0" + }, + "bin": { + "concurrently": "bin/concurrently.js" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/concurrently/node_modules/rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "dev": true, + "dependencies": { + "tslib": "^1.9.0" + }, + "engines": { + "npm": ">=2.0.0" + } + }, + "node_modules/concurrently/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/concurrently/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "node_modules/consola": { + "version": "2.15.3", + "resolved": "https://registry.npmjs.org/consola/-/consola-2.15.3.tgz", + "integrity": "sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw==", + "dev": true + }, + "node_modules/console.table": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/console.table/-/console.table-0.10.0.tgz", + "integrity": "sha512-dPyZofqggxuvSf7WXvNjuRfnsOk1YazkVP8FdxH4tcH2c37wc79/Yl6Bhr7Lsu00KMgy2ql/qCMuNu8xctZM8g==", + "dev": true, + "dependencies": { + "easy-table": "1.1.0" + }, + "engines": { + "node": "> 0.10" + } + }, "node_modules/create-require": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==" }, + "node_modules/date-fns": { + "version": "2.30.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz", + "integrity": "sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.21.0" + }, + "engines": { + "node": ">=0.11" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/date-fns" + } + }, + "node_modules/defaults": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", + "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", + "dev": true, + "dependencies": { + "clone": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -173,6 +700,21 @@ "url": "https://github.com/motdotla/dotenv?sponsor=1" } }, + "node_modules/easy-table": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/easy-table/-/easy-table-1.1.0.tgz", + "integrity": "sha512-oq33hWOSSnl2Hoh00tZWaIPi1ievrD9aFG82/IgjlycAnW9hHx5PkJiXpxPsgEE+H7BsbVQXFVFST8TEXS6/pA==", + "dev": true, + "optionalDependencies": { + "wcwidth": ">=1.0.1" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, "node_modules/entities": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/entities/-/entities-3.0.1.tgz", @@ -184,6 +726,59 @@ "url": "https://github.com/fb55/entities?sponsor=1" } }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/external-editor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "dev": true, + "dependencies": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/fast-safe-stringify": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", + "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==", + "dev": true + }, + "node_modules/figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "dev": true, + "dependencies": { + "escape-string-regexp": "^1.0.5" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/follow-redirects": { "version": "1.15.3", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.3.tgz", @@ -216,6 +811,195 @@ "node": ">= 6" } }, + "node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/inquirer": { + "version": "8.2.5", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.5.tgz", + "integrity": "sha512-QAgPDQMEgrDssk1XiwwHoOGYF9BAbUcc1+j+FhEvaOt8/cKRqyLn0U5qA6F74fGhTMGxf92pOvPBeh29jQJDTQ==", + "dev": true, + "dependencies": { + "ansi-escapes": "^4.2.1", + "chalk": "^4.1.1", + "cli-cursor": "^3.1.0", + "cli-width": "^3.0.0", + "external-editor": "^3.0.3", + "figures": "^3.0.0", + "lodash": "^4.17.21", + "mute-stream": "0.0.8", + "ora": "^5.4.1", + "run-async": "^2.4.0", + "rxjs": "^7.5.5", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "through": "^2.3.6", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-interactive": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", + "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/iterare": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/iterare/-/iterare-1.2.1.tgz", + "integrity": "sha512-RKYVTCjAnRthyJes037NX/IiqeidgN1xc3j1RjFfECFp28A1GVwK9nA+i0rJPaHqSZwygLzRnFlzUuHFoWWy+Q==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, "node_modules/linkify-it": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-4.0.1.tgz", @@ -224,6 +1008,27 @@ "uc.micro": "^1.0.1" } }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/make-error": { "version": "1.3.6", "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", @@ -268,6 +1073,27 @@ "node": ">= 0.6" } }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/minimist": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", @@ -276,6 +1102,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", + "dev": true + }, "node_modules/node-fetch": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", @@ -295,6 +1127,77 @@ } } }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", + "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", + "dev": true, + "dependencies": { + "bl": "^4.1.0", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.5.0", + "is-interactive": "^1.0.0", + "is-unicode-supported": "^0.1.0", + "log-symbols": "^4.1.0", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-to-regexp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-3.2.0.tgz", + "integrity": "sha512-jczvQbCUS7XmS7o+y1aEO9OBVFeZBQ1MDSEqmO7xSoPgOPoowY/SxLpZ6Vh97/8qHZOteiCKb7gkG9gA2ZUxJA==", + "dev": true + }, "node_modules/property-expr": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/property-expr/-/property-expr-2.0.5.tgz", @@ -305,16 +1208,200 @@ "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/reflect-metadata": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz", + "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==", + "dev": true + }, + "node_modules/regenerator-runtime": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz", + "integrity": "sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==", + "dev": true + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/run-async": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", + "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/rxjs": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.0.tgz", + "integrity": "sha512-F2+gxDshqmIub1KdvZkaEfGDwLNpPvk9Fs6LD/MyQxNgMds/WH9OdDDXOmxUZpME+iSK3rQCctkL0DYyytUqMg==", + "dev": true, + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/rxjs/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "dev": true + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "node_modules/spawn-command": { + "version": "0.0.2-1", + "resolved": "https://registry.npmjs.org/spawn-command/-/spawn-command-0.0.2-1.tgz", + "integrity": "sha512-n98l9E2RMSJ9ON1AKisHzz7V42VDiBQGY6PB1BwRglz99wpVsSuGzQ+jOi6lFXBGVTCrRpltvjm+/XA+tpeJrg==", + "dev": true + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", + "dev": true + }, "node_modules/tiny-case": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/tiny-case/-/tiny-case-1.0.3.tgz", "integrity": "sha512-Eet/eeMhkO6TX8mnUteS9zgPbUMQa4I6Kkp5ORiBD5476/m+PIRiumP5tmh5ioJpH7k51Kehawy2UDfsnxxY8Q==" }, + "node_modules/tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "dependencies": { + "os-tmpdir": "~1.0.2" + }, + "engines": { + "node": ">=0.6.0" + } + }, "node_modules/toposort": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/toposort/-/toposort-2.0.2.tgz", "integrity": "sha512-0a5EOkAUp8D4moMi2W8ZF8jcga7BgZd91O/yabJCFY8az+XSzeGyTKs0Aoo897iV1Nj6guFq8orWDS96z91oGg==" }, + "node_modules/tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", + "dev": true, + "bin": { + "tree-kill": "cli.js" + } + }, "node_modules/ts-node": { "version": "10.9.1", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", @@ -357,6 +1444,12 @@ } } }, + "node_modules/tslib": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.3.tgz", + "integrity": "sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ==", + "dev": true + }, "node_modules/type-fest": { "version": "2.19.0", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", @@ -386,11 +1479,47 @@ "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz", "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==" }, + "node_modules/uid": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/uid/-/uid-2.0.1.tgz", + "integrity": "sha512-PF+1AnZgycpAIEmNtjxGBVmKbZAQguaa4pBUq6KNaGEcpzZ2klCNZLM34tsjp76maN00TttiiUf6zkIBpJQm2A==", + "dev": true, + "dependencies": { + "@lukeed/csprng": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true + }, "node_modules/v8-compile-cache-lib": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==" }, + "node_modules/wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", + "dev": true, + "dependencies": { + "defaults": "^1.0.3" + } + }, "node_modules/whatwg-url": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", @@ -410,6 +1539,65 @@ "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true, + "engines": { + "node": ">=10" + } + }, "node_modules/yn": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", diff --git a/package.json b/package.json index cb1ce88c6..012eff3f2 100644 --- a/package.json +++ b/package.json @@ -7,6 +7,7 @@ "remove-stoplight-tags-from-openapi": "ts-node ./scripts/remove-stoplight-tags-from-openapi.ts", "remove-deprecated-from-openapi": "ts-node ./scripts/remove-deprecated-from-openapi.ts", "generate-open-api-with-specific-endpoints": "ts-node ./scripts/generate-open-api-with-specific-endpoints.ts", + "generate-open-api-with-specific-endpoints-remove-additionalProperties-if": "ts-node ./scripts/generate-open-api-with-specific-endpoints-remove-additionalProperties-if.ts", "manage-project": "ts-node ./scripts/build-md-tables-from-openapi.ts && ts-node ./scripts/manage-project.ts", "readme-upload-missing-images": "ts-node ./scripts/readme-upload-missing-images.ts" }, @@ -14,6 +15,7 @@ "axios": "^1.5.1", "colors": "^1.4.0", "dotenv": "^16.3.1", + "lodash": "^4.17.21", "markdown-it": "^13.0.2", "minimist": "^1.2.8", "node-fetch": "^2.7.0", @@ -21,6 +23,8 @@ "yup": "^1.1.1" }, "devDependencies": { + "@openapitools/openapi-generator-cli": "^2.7.0", + "@types/lodash": "^4.14.202", "@types/node": "^20.7.0" } } diff --git a/scripts/generate-open-api-with-specific-endpoints-remove-additionalProperties-if.ts b/scripts/generate-open-api-with-specific-endpoints-remove-additionalProperties-if.ts new file mode 100644 index 000000000..9c9311065 --- /dev/null +++ b/scripts/generate-open-api-with-specific-endpoints-remove-additionalProperties-if.ts @@ -0,0 +1,192 @@ +// to run it: +// npm run generate-open-api-with-specific-endpoints-remove-additionalProperties-if -- --always +// or +// npm run generate-open-api-with-specific-endpoints-remove-additionalProperties-if -- --usedWithStandardProperties + +import fsPromises from "fs/promises"; +import fs from "fs"; +import path from "path"; +import { omit } from "lodash"; +import minimist from "minimist"; +import colors from "colors"; +const options = minimist(process.argv.slice(2)); + +function isObject(value) { + return typeof value === "object" && value !== null && !Array.isArray(value); +} + +const removeStoplightTag = (node: object): object => { + delete node["x-stoplight"]; + for (const attr in node) { + if (isObject(node[attr])) { + removeStoplightTag(node[attr]); + } + } + return node; +}; + +const grabList: { endpoint: string; methods: string[] | true }[] = [ + { endpoint: "/v1/customers/{customerId}/consents", methods: ["put"] }, +]; + +const removeAdditionalProperties = ( + e: any, + keepIfPropertiesNotPresent = false +) => { + const simplifyObjectModel = (o) => + omit(o, ["additionalProperties", "properties"]); + + if (e instanceof Object) { + if ( + !keepIfPropertiesNotPresent && + "additionalProperties" in e && + e.additionalProperties instanceof Object + ) { + return simplifyObjectModel(e); + } else if ( + "additionalProperties" in e && + e.additionalProperties instanceof Object && + "properties" in e + ) { + return simplifyObjectModel(e); + } + for (const f of Object.keys(e)) { + if (typeof e[f] === "object") { + e[f] = removeAdditionalProperties(e[f]); + } + } + } + return e; +}; + +const main = async (keepIfPropertiesNotPresent) => { + const openApiPath = path.join(__dirname, "../reference/OpenAPI.json"); + const openAPIContent = JSON.parse( + (await fsPromises.readFile(openApiPath)).toString() + ); + removeStoplightTag(openAPIContent); + + // Removing deprecated paths + const paths = {}; + const pathsKeys = Object.keys(openAPIContent.paths); + for (const pathKey of pathsKeys) { + const grab = grabList.find((grab) => grab.endpoint === pathKey); + const path = {}; + const methods = Object.keys(openAPIContent.paths[pathKey]); + for (const method of methods) { + if ( + grab?.methods === true || + (grab?.methods && grab.methods.includes(method)) || + (grab?.methods && method === "parameters") + ) { + path[method] = openAPIContent.paths[pathKey][method]; + } else { + continue; + } + } + if (Object.keys(path).length > 0) { + paths[pathKey] = path; + } + } + + const pathsStringify = JSON.stringify(paths); + + // Removing not used parameters + const parametersNames = + pathsStringify + .match(/"#\/components\/parameters\/.*?"/g) + ?.map((match) => + match.replace('"#/components/parameters/', "").slice(0, -1) + ) + .sort() || []; + const parameters = {}; + for (const parameterName of parametersNames) { + if (!openAPIContent.components.parameters?.[parameterName]) { + console.log(`not found ${parameterName} in parameters`); + continue; + } + parameters[parameterName] = removeAdditionalProperties( + openAPIContent.components.parameters[parameterName], + keepIfPropertiesNotPresent + ); + } + + // Removing not used schemas + const schemas = {}; + const schemasNames = + pathsStringify + .match(/"#\/components\/schemas\/.*?"/g) + ?.map((match) => match.replace('"#/components/schemas/', "").slice(0, -1)) + .sort() || []; + for (const schemaName of schemasNames) { + if (!openAPIContent.components.schemas?.[schemaName]) { + console.log(`not found ${schemaName} in schemas`); + continue; + } + schemas[schemaName] = removeAdditionalProperties( + openAPIContent.components.schemas[schemaName], + keepIfPropertiesNotPresent + ); + } + + // Finding other schemas uses + let lastSchemaStringify = ""; + while (true) { + const schemaStringify = JSON.stringify(schemas); + if (lastSchemaStringify === schemaStringify) { + break; + } + lastSchemaStringify = schemaStringify; + const schemasNames = + schemaStringify + .match(/"#\/components\/schemas\/.*?"/g) + ?.map((match) => + match.replace('"#/components/schemas/', "").slice(0, -1) + ) + .sort() || []; + for (const schemaName of schemasNames) { + if (!openAPIContent.components.schemas?.[schemaName]) { + console.log(`not found ${schemaName} in schemas`); + continue; + } + schemas[schemaName] = openAPIContent.components.schemas[schemaName]; + } + } + + // Building all together + const newOpenApiFile = { ...openAPIContent }; + newOpenApiFile.components.parameters = parameters; + newOpenApiFile.components.schemas = schemas; + newOpenApiFile.paths = paths; + + //write the new OpenApiFile + const pathToTmp = path.join(__dirname, "../tmp"); + if (!fs.existsSync(pathToTmp)) { + fs.mkdirSync(pathToTmp); + } + const pathToTmpReference = path.join(__dirname, "../tmp/referencePartial"); + if (!fs.existsSync(pathToTmpReference)) { + fs.mkdirSync(pathToTmpReference); + } + await fsPromises.writeFile( + path.join(__dirname, "../tmp/referencePartial/OpenAPI.json"), + JSON.stringify(newOpenApiFile, null, 2) + ); +}; + +const { always, usedWithStandardProperties } = options; +if (!always && !usedWithStandardProperties) { + console.log( + colors.red( + "invalid arguments, missing `always` or `usedWithStandardProperties`" + ) + ); +} else if (always && usedWithStandardProperties) { + console.log( + colors.red( + "invalid arguments, provided `always` and `usedWithStandardProperties`, please provide one argument" + ) + ); +} else { + main(!!usedWithStandardProperties); +} diff --git a/scripts/generate-open-api-with-specific-endpoints.ts b/scripts/generate-open-api-with-specific-endpoints.ts index f48b2a877..9c7bd2905 100644 --- a/scripts/generate-open-api-with-specific-endpoints.ts +++ b/scripts/generate-open-api-with-specific-endpoints.ts @@ -17,7 +17,7 @@ const removeStoplightTag = (node: object): object => { }; const grabList: { endpoint: string; methods: string[] | true }[] = [ - { endpoint: "/v1/qualifications", methods: true }, + { endpoint: "/v1/customers/{customerId}/consents", methods: ["put"] }, ]; const main = async () => { @@ -37,7 +37,8 @@ const main = async () => { for (const method of methods) { if ( grab?.methods === true || - (grab?.methods && grab.methods.includes(method)) + (grab?.methods && grab.methods.includes(method)) || + (grab?.methods && method === "parameters") ) { path[method] = openAPIContent.paths[pathKey][method]; } else {