Skip to main content
Version: 12.x (Current)

Fast Data Low Code

Fast Data is an event-driven architecture that allows you to collect data, manipulate and aggregate it from your systems in near real-time using a data stream approach.

What We Will Build

In this tutorial, we will create a simple example of a Single View dedicated to a history of orders made by a customer. To do that we will expect to receive data from two different tables: a table that includes a registry of customer data (like the name, address and so on) and a table for a single order (with date, price and the identifier of the customer included in the registry).

We are going also to create the microservices that will store the data received in separate collections (we will call them Projections) and aggregate the Single View.

To do that, in the console, you will:

  • Generate two Projections where we will store the data;
  • Connect two endpoints to the Projections;
  • Create a Single View Low Code that will aggregate the data from the two projections;
  • Create an endpoint that will consume the data from the Single View;
  • Test the Single View.

Prerequisites

Before starting this tutorial, you need to be familiar with the concepts of Fast Data architecture, including Projections, Single View, and Kafka (including producers and consumers). For the technical part of this tutorial, an up-and-running Kafka cluster is required.

System of Record

A System of Record is a container of Projections, which is are architectural objects responsible for storing the data in a persistent storage, to have a fast and scalable way to decouple the data from the source system. We will use these projections to have an easy way to read a 1:1 copy of the data from the source systems, and later they will be used to aggregate the data from the source systems, using the Single View Creator.

The architectural component responsible for creating and updating the projection is the Real-Time Updater service. To simplify, this microservice is a Kafka consumer that will consume the data from the source systems and will update the projection.

In this tutorial, we can imagine a simple delivery platform where the data are the registry and the order of the customers. So, we will create a single System of Record with two projections (one for the registry and one for the order) attached to a single Real-Time Updater.

Microservice Real-Time Updater

info

From version 11.7.0 of the Console, the Real-Time Updater service must be created manually with the guide explained in this paragraph.

If you're using an older version, you can skip this paragraph since the service is automatically created when saving the configuration after creating the System of Record.

The first step is to create the microservice Real-Time Updater, that will be attached to the System of Record we will create. To do so, we move to the Microservices section of the console. From here we can click on the "Create a Microservice" button on the top-left corner to open the Marketplace and select our microservice.

We can find it under the name of Real-Time Updater.

Create Order Projection

After selecting the Real-Time Updater service, the only thing you need to choose is a unique name (we suggest fast-data-delivery-realtime-updater) and, eventually the description. Once clicked on the "Create" button, the service will be created and we will be redirected to the service page. Now we need to configure all the parameters of the microservice. Here is an example of the fields that we need to configure:

  • PROJECTIONS_DATABASE_NAME: the name of the database that will be used to store the data of the Projections
  • PROJECTIONS_CHANGES_COLLECTION_NAME: the name of the collection that will be used to store the changes of the Projections (we suggest fd-pc-delivery)
  • KAFKA_BROKERS: the list of the kafka brokers that will be used to send the data to the Projections
  • KAFKA_GROUP_ID: the group id that will be used to consume the data from the source systems
  • KAFKA_SASL_USERNAME: the username that will be used to login to the kafka brokers
  • KAFKA_SASL_PASSWORD: the password that will be used to login to the kafka brokers
  • KAFKA_SASL_MECHANISM: the mechanism that will be used to login to the kafka brokers
info

The Real-Time Updater includes more optional parameters that might be included via Environment Variables. If you need to know more about them, you can read the documentation of the Real-Time Updater.

Create the System of Record

Before starting to create projections, we need to create the System of Record (SoR) representing the source system we want to connect to.

To create a SoR follow the steps below:

  1. Open the Systems of Record page in the Fast Data section of our left menu;
  2. Click on the button "Create new System of Record": Page System of Record
  3. In the dialog, choose the name (which will act as an identifier of the System of Record, we will call it delivery) and the Kafka message format (choose the format you want to use for the messages sent on the Kafka topics) and then click on "Go to system page": Create System of Record
  4. Now we can create the projections inside this System of Record: Page Projections

Registry Projection

The first projection we will create is the registry and it will store the data of the registries.

Inside a System of Record page, we need to click on the "Create new Projection" button. A modal will open allowing the user to choose the Projection name. Click on "Create" to create the Projection. Create System of Record

Once the projection is created, you will be automatically redirected to the page of the registry projection. On this page, we can see its data schema and other configurations related to it. Create System of Record

Fields

Now we need to define the fields that we want to store in the Projection. You can do that by clicking the "Add field" button located at the top-right of the "Fields card".

Create Projection Fields

info

It is mandatory to define at least one custom field. Also, you must define at least one field as Primary Key (select the related checkbox).

info

You can leave defaultIdentity as a value for Cast Function when entering the fields for this tutorial. In this way, every value inside the payload received from the Source Systems will be saved in the database without further customizations.

For the scope of this tutorial, we ask you to add the following fields:

FieldTypeDescription
registry_idStringThe registry ID. Check this field as "Required" and "Primary Key".
nameStringThe name of the customer to whom the registry refers.
emailStringThe email of the customer to whom the registry refers.
phoneStringThe phone of the customer to whom the registry refers.
addressStringThe address of the customer to whom the registry refers.
cityStringThe city of the customer to whom the registry refers.
stateStringThe State of the customer to whom the registry refers.

Ingestion Topics

At the bottom of the same page, we can also see the Kafka topics for each environment. There are two types of topics (Ingestion topics and PR Update topics) but within the scope of this tutorial, only the ingestion topics are considered. The Kafka Ingestion topics will be used by the Real-Time Updater to receive the data from the Source Systems so that the Projections will be updated, but we will discuss this later. Create System of Record

These are the topics defined in Kafka where we expect to receive messages related to the update of projections. The following are the suggested names for the Ingestion topics:

  • demo.development.delivery.registry.ingestion: the topic that will be used to send the data to the registry projection in the development environment
  • demo.integration.delivery.registry.ingestion: the topic that will be used to send the data to the registry projection in the integration environment
  • demo.production.delivery.registry.ingestion: the topic that will be used to send the data to the registry projection in the production environment

The Kafka topic names can be changed in the configuration, but please mind that their name must be unique, and thus cannot be reused in other projections.

info

The application does not create Kafka topics for you. If you decide to use a different Kafka topic name, please be sure the topic already exists and it is fully operational. The Real-Time Updater, the service that receives the update from the source System, will crash at startup if there is a topic not defined in Kafka.

Order Projection

The second projection we will create is the order one. This projection will store the data of the user's orders. Again, inside a System of Record page, we need to click on the button "Create new Projection", input order as the projection name and click on "Create".

Create Order Projection

Once the projection is created, we can see the page of the order Projection.

Order Projection Overview

Fields

Similar to the registry Projection, we ask you to add the following fields:

FieldTypeDescription
order_idStringThe order ID. Check this field as "Required" and "Primary Key".
nameStringThe name of the product.
priceNumberThe price of the product.
dateStringThe date when the order was placed.
registry_idStringThe registry ID of the customer who placed the order.

Ingestion Topics

These are the topics defined in Kafka where we expect to receive messages related to the update of projections. The following are the suggested names for the Ingestion topics:

  • demo.development.delivery.order.ingestion: the topic that will be used to send the data to the order projection in the development environment
  • demo.integration.delivery.order.ingestion: the topic that will be used to send the data to the order projection in the integration environment
  • demo.production.delivery.order.ingestion: the topic that will be used to send the data to the order projection in the production environment

Attach the Real-Time Updater to the System of Record

info

Attaching a service to a System of Record is required only with version 11.7.0 of the Console or above. Otherwise, the service will be automatically created and attached to the System

The last step to finish the configuration of the System of Record is to attach the Real-Time Updater we originally created. To do so, move to the Services tab where a placeholder will inform you that no services have been attached yet and suggest you to select one from the list of compatible services.

Attach Real-Time Updater

After choosing the fast-data-delivery-realtime-updater and clicking to "Attach service" a new page will inform you that the service have been attached successfully. You can enter inside the service page (by clicking the arrow button on the right side of the table) to finalize the configuration by selecting the projections that will be managed by the Real-Time Updater (in our case, both Registry and Order projection).

Then, we are ready to save the configuration.

Configure Real-Time Updater ConfigMaps

After saving the configuration, we can return to the fast-data-delivery-realtime-updater microservice to notice several differences: some environment variables have been modified (such as the INVARIANT_TOPIC_MAP, SYSTEM_OF_RECORDS) and several new ConfigMaps have been created.

Real-Time Updater Microservice

Among these, two of them needs to be configured:

  • The ER Schema, which is a ConfigMap in JSON format that represents a classic entity-relationship model, so it is used to know how the data of different projections are connected.
  • The Projection Changes Schema, which is a ConfigMap in JSON format that represents the path that goes from one projection to another and will be used to find the Single View key (in this tutorial it will be registry_id) that needs to be updated.

The goal of these configurations is to create a relationship from the order Projection to the registry Projection. So whenever an Order or a Registry is updated, we will store it in a dedicated collection in MongoDB that a Single View associated with that Registry has to be re-aggregated (so, updated with the latest values of the Projections).

ER Schema

In our example, the ER Schema ConfigMap will define a relationship where one Customer (stored in the registry Projection) can have multiple Orders (stored in the order Projection).

ER Schema of Registry and Order

You can configure the ER Schema in no code or low code mode:

info

The following explains how to configure an ER Schema using the "No Code" feature. To do so, please make sure that this feature is enabled in your project. You might need to ask your Project Administrator for it.

To configure the ER Schema with the No Code, you need to return to the "Projection" section, and then select the "ER Schemas" tab. From there, you can create a new ER Schema, by clicking the "Create new ER Schema" button: a modal will prompt you to choose a name for your ER Schema (we suggest: delivery-er-schema). After that, you will be redirected to the ER Schema canvas.

Create new ER Schema

The configuration of the ER Schema with the "No Code" is explained in detail in the dedicated section, but we include here a quick guide on how to create the ER Schema with the needed relationships:

  • In the right side panel you can see the list of Projections inside the System of Record delivery: drag the collections registry and order to the canvas in the middle of the screen.
  • Inside the canvas, draw a line from the dot at the right of the registry Projection to the order Projection: you'll see that the line remains visible with a small "!" icon in the middle, which means that the relationship has been created but the condition inside is empty.
  • the right side panel should show the details of the relationship. From here you'll need to:
    • change the "Condition Type" value of "A -> B" to One to many.
    • click on the "Edit Rules" button to open the Rules Modal: click on "+ Rule" and then fill the input fields with "registryid" both for the _registry and the order Projection. Finally, click the "Save" button.

The final result should resemble the following image:

The ER Schema configuration is completed

After that, the ER Schema is already completely configured. To be used in the Real-Time Updater microservice, however, you need to copy the JSON code (visible by clicking on the "\<\/>" button at the top-left side of the canvas) and paste it in the ER Schema ConfigMap of the microservice (from the "Microservices" section, select the Real-Time Updater named fast-data-delivery-realtime-updater, go to the "ConfigMaps & Secret" and select the "er-schema" tab).

Projection Changes Schema

The Projection Changes Schema is a ConfigMap that represents the path that goes from one entity to another, and it is used to find the Single View key. We will talk about the Single View key in the next section, but remember that the Single View key is the primary key of our Single View, and we need to obtain the Single View key from the updated Projection (or that follows the ER Schema until we reach a projection that contains the Single View key).

The reason why we need to configure this ConfigMap is that, when we will update each Projection, we need to know the Single View keys to update.

Inside our example we can see that the Single View key is the registry_id, so we need to configure the Projection Changes Schema to know each path that goes from the projection to each other projection that contains the Single View key. In this example, we have the registry_id in both the Registry Projection and in the Order Projection, so we need to configure the Projection Changes Schema to read the registry_id from the updated Projection.

{
"version": "1.0.0",
"config": {
"order-history": {
"paths": [
{
"path": [ "order", "registry" ],
"identifier": {
"registry_id": "registry_id"
}
}
]
}
}
}

With this configuration, the Single View that we are going to create can be updated with both of the following ways:

  • if we update the Projection registry, then we can read the registry_id directly from there.
  • if we update the Projection order, we will search the related document inside the Projection registry, then we can read the registry_id.

You can find a more thorough explanation of the Projection Changes Schema on the dedicated page of the documentation.

Expose Projection Endpoints

At this point, we have configured the microservice, we already have one System of Record and two projections, but we need to expose the Projection endpoints. It is not mandatory to expose the Projection endpoints to create, update and work with the Single View, but if you want to expose the raw data stored in the projection you need to create the endpoints.

In the Endpoint section, we can click on the Create new endpoint button to create the Projection's endpoints. Create Endpoint&#39;s Projection

Inside the creation page, we can choose the endpoint path. Inside the type we need to select "Fast Data Projection" and, in the Fast Data Projection dropdown menu, we can choose the Projection that we want to expose. We can create two different endpoints, one for the registry and one for the order.

info

Remember that the endpoint connected to the Fast Data will create just the read endpoint: so, we can't create, update, and delete the data stored in the Projection using a REST request.

Testing Projection

Now it's time to save and deploy the new configuration. Deploy the configuration

After that, we can try to send a message to the Kafka Topic created in the previous steps, and we can see the data stored in the Projection using the APIs available in the API Documentations.

Following are example responses from the API Documentations for Registry and Order Projections after the Kafka messages have been successfully processed.

Here is the response for the Registry Projection after we created a customer with id reg-1.

[
{
"_id": "63ceb97233241d6536c857e759c02",
"__STATE__": "PUBLIC",
"address": "customer address",
"city": "customer city",
"createdAt": "2023-01-23T16:44:33.006Z",
"email": "customer1@mail.com",
"name": "customer 1",
"phone": "customer phone",
"registry_id": "reg-1",
"state": "customer state",
"updatedAt": "2023-01-23T16:44:33.006Z"
}
]

Here is the response for the Order Projection after we created an order with id ord-1.

[
{
"_id": "63cebcb6d65321231236c857e76b6ab",
"__STATE__": "PUBLIC",
"createdAt": "2023-01-23T16:58:30.201Z",
"date": "23/1/2023",
"name": "order-1",
"order_id": "ord-1",
"price": "123",
"registry_id": "reg-1",
"updatedAt": "2023-01-23T16:58:30.201Z"
}
]

Single View

The Single View is an architectural object that it's used to store the aggregate data from the Projections. The goal of the Single View is to store the data already aggregated and ready to use from the Projection. A good approach is to store the data in Single Views dedicated to each channel and each use. For example, if inside the client we use the data from the registry and the order in the same interface, we can create a Single View called "registry_and_order" and store all the useful data in this Single View.

In this tutorial, our Single View is called order-history and we will create an object schema with the registry information and the orders information linked to the registry.

Microservice Single View Creator Configuration

First of all, we need to create a microservice that we will attach to the Single View that we're going to create. This microservice is called "Single View Creator", and it's available in the microservice Marketplace. Marketplace and Single View Creator

Here we have two different plugins: the first one is for the Single View Creator, and the second one is for the Single View Creator Low Code. We will go for the latter since we want to leverage the usage of ConfigMaps for a quicker configuration of the microservice. We suggest naming it single-view-creator-delivery.

Now we need to configure all the parameters of the microservice, here is an example of the fields that we need to configure:

  • PROJECTIONS_CHANGES_COLLECTION: it is the name of the collection that includes the Projection Changes generated by the Real-Time Updater, and it must have the same value set in that microservice (we previously suggested fd-pc-delivery).
  • SINGLE_VIEWS_COLLECTION: should be the name of the single view which your single view creator is responsible for (we suggest order-history, the same name as the Single View we are going to create).
  • SINGLE_VIEWS_ERRORS_COLLECTION: it is the name of a MongoDB CRUD you want to use as a collection for the single view errors (we suggest: order-history-errors).
  • SINGLE_VIEWS_PORTFOLIO_ORIGIN: a representative name used in debugging and metrics to identify the source of any Single View update request (we suggest to use the name of the System of Record, delivery).
  • TYPE: identifies the type of projection changes that need to be read. It should be the same as the Single View name you want to update (also here we are going to call it order-history).
info

The Single View Creator includes more optional parameters that might be included via Environment Variables. If you need to know more about them, you can refer to the related documentation section.

The microservice also needs the following ConfigMaps to work properly:

  • Single View Key: this is the key that will be used to store the Single View
  • ER-Schema: the ER Schema (should be the same as the one used in the Projection Microservice)
  • Aggregation: the aggregation that will be used to calculate the Single View

But we are going to update these later, inside the Single View page.

Single View "order-history"

Inside our Single View, each document is an object that contains the registry information about the user and the information about the orders associated with the user.

We need to open the Single Views page in the Fast Data section of our left menu, then click on the Create new Single View button, and we can create a new Single View called "order-history". Single View

Here we can choose the name of the Single View. Create New Single View

Fields

Now we need to create the fields of the Single View, inside this tutorial we will create the following fields:

FieldTypeDescription
registry_idStringThe registry ID. This field must be marked as "Required".
nameStringThe name of the customer to whom the registry refers.
cityStringThe city of the customer to whom the registry refers.
stateStringThe State of the customer to whom the registry refers.
ordersArray of objectsThe orders associated to the registry.

It is important to also define the JSON Schema for the order field of the Single View. You can edit the field and add the following JSON to the order field:

{
"properties": {
"id": {
"title": "Order id",
"type": "string"
},
"price": {
"title": "Order Price",
"type": "number"
},
"name": {
"title": "Order name",
"type": "string"
},
"date": {
"type": "string",
"format": "date"
}
},
"additionalProperties": false,
"type": "object"
}
info

We can of course create more fields: here we omitted the email, phone and address fields because we don't need to expose them in our Single View.

Single View Fields

Strategies

Now we need to create the strategies of the Single View. Strategies are used to define when the data need to be updated in a Single View. For example, when we update the registry we need to update the Single View information. In this tutorial, we will create two strategies: the first one is for the registry's update and the second one is for the order's update.

Inside our Single View "order-history", click on the Add link to strategy button. Strategy Page

Now we will choose the System of Record and the Projection that we want to link to the Strategy; in our case, "delivery" and "registry". Strategy Page

Last but not least, we need to choose the Strategy Source. In this case, we will use the "Low Code" strategy, then we can click on the Create button. Strategy Page

Then we can create the strategy also to the Order Projection, following the same process. Final Strategy Page

info

We need to define one strategy for each Projection because when the projection is affected by a change, we need to calculate which single views are impacted. This is made possible by the strategies. With the "Low Code Strategy", the system will automatically calculate the impacted single views.

Attach Single View Creator to Single View

The last configuration that you need to do is to attach the newly created Single View Creator microservice to our order-history Single View.

Inside the Single View Page, go to the "Single View Creators" tab. A placeholder will inform the user that a Single View Creator must be attached.

Attach Single View Creator

From there, select the single-view-creator-delivery, then click the "Add single view creator" button. The selected Single View Creator microservice will show up on the page, and the "->" button at the right side of the table will open a new page where it will be possible to configure the ConfigMaps.

ER Schema

The ER-Schema is the same one we used in the Projection Microservice. We can duplicate the configMap, or we can use a Shared ConfigMap.

info

The following explains how to configure an ER Schema for the Single View Creator microservice using the "No Code" feature. Please make sure that your project has activated this feature. You might need to ask your Project Administrator for it.

If we've used the "No Code" feature before, the ER Schema will be already saved as delivery-er-schema. Inside the ER Schema tab, there will be a dropdown field from which we can select the delivery-er-schema. By doing that, the ConfigMap will already be completely configured.

Aggregation

The aggregation is the configMap that will be used to define the rules on how the Single View will be calculated. In this configuration, we will get our dependencies from the projection and the description of how we want to map the dependencies to the Single View Data Model.

In our case, we will map registry_id, name, city and state from the Projection registry, and the order list from the Projection order.

info

The following explains how to configure an Aggregation using the "No Code" feature, available from version 11.3.0 of the Mia-Platform Console. Please make sure that your project has activated this feature. You might need to ask your Project Administrator for it.

To have an exhaustive explanation of how to configure an Aggregation with the "No Code" feature, you can refer to the specific documentation page.

Assuming that the ER Schema has already been selected (see previous paragraph), here is a list of steps to be executed to configure the Aggregation:

  • Selecting the "Aggregation" tab, you'll be required to select a Base Projection which is the Projection from where the mapping will begin: in our tutorial, we select the Projection registry and click on "Set base projection". After this, the Single View Data Model will show up to the left, ready for the configuration.
  • Select the field registry_id: a panel will open to the right where we can select the dependency registry and the field registry_id.
  • Select the field name: a panel will open to the right where we can select the dependency registry and the field name.
  • Select the field city: a panel will open to the right where we can select the dependency registry and the field city.
  • Select the field state: a panel will open to the right where we can select the dependency registry and the field state.
  • Select the field orders: it will open a panel with the list of fields inside the Array of Objects orders defined in the Data Model.
  • Click on the "Edit dependencies (0)" to create the dependency between registry and order in the Aggregation: inside the modal click first on the "Add dependency" button to start configuring the dependency.
  • Select the Source Projection registry: the rest of the form should fill automatically and a click on the "Add dependency" button will generate the dependency.
  • Click on "Save" to update the Aggregation with the new dependency.
  • Select the field id: a panel will open to the right where we can select the dependency order and the field order_id.
  • Select the field price: a panel will open to the right where we can select the dependency order and the field price.
  • Select the field name: a panel will open to the right where we can select the dependency order and the field name.
  • Select the field date: a panel will open to the right where we can select the dependency order and the field date.

After the following steps, the Aggregation is completely configured and applied to the ConfigMap. You can click on the toggle "Advanced Mode" at the bottom-left of the page to review your configuration in a JSON format.

info

in case you want to reset the aggregation by changing the Base Projection, you have to go to the Settings tab and click the "Edit" button inside the "General" card.

Single View Key

The Single View Key is a ConfigMap in JSON format to create a relationship between the identifier of the projection change and the Single View fields to get a query that, executed in the Single View collection, retrieves the Single View document to be updated.

info

The following explains how to configure a Single View Key using the "No Code" feature, available from version 11.3.0 of the Mia-Platform Console. Please make sure that your project has activated this feature. You might need to ask your Project Administrator for it.

To configure the Single View Key with the "No Code" feature, you can refer to the specific documentation page.

Here is a quick list of operations to execute to configure correctly the Single View Key:

  • move to the "Settings" page and go to the "Single View Key" card.
  • click on the "Configure" button to open a modal to start configuring the Single View Key.
  • click the "Add Single View Key": a new line will appear with two input fields.
  • as Single View Primary Field, which is a field of the Single View, select registry_id.
  • as Projection Identifier, which is the projection identifier defined in the Projection Changes Schema, select registry_id (which is, for our example, the same name of the Base Projection registry).
  • you can review the updates by clicking the toggle "Advanced Mode" and, when you are sure everything is fine, click on "Save".

Now also the Single View Key ConfigMap is completely configured.

Expose Single View Endpoints

Now we have the Projections, the Single View and each configuration ready to be used, we need to expose the Single View endpoints.

  1. Return to the Endpoints menu on the left;
  2. Select Create new endpoint;
  3. Choose the Base Path;
  4. Choose Single View as Type;
  5. Select from the list the name of our Single View, in this case: /order-history.

Expose Single View

Now we can commit our changes, deploy, and try our Fast Data sending data from the Kafka Topic, and see the result in the Single View using the endpoints.

Testing Single View

You can now consume the order-history Single View using the APIs available in the API Documentations.

Here you can see the response for the order-history Single View for the same data we used for testing the Projections. We can see that ord-1 order is exposed in the orders array for reg-1 customer.

[
{
"_id": "63ceb97fd6536c857e75a0f2",
"__STATE__": "PUBLIC",
"updatedAt": "2023-01-23T16:58:47.805Z",
"registry_id": "reg-1",
"name": "customer 1",
"city": "customer city",
"state": "customer state",
"orders": [
{
"id": "ord-1",
"price": "123",
"name": "order-1",
"date": "23/1/2023"
}
]
}
]

And here you can see the response with more customers and orders. The customer reg-1 has now more orders that are aggregated in the orders list.

[
{
"_id": "63ceb97f123123d6536c857e75a0f2",
"__STATE__": "PUBLIC",
"updatedAt": "2023-01-23T17:06:47.845Z",
"registry_id": "reg-1",
"name": "customer 1",
"city": "customer city",
"state": "customer state",
"orders": [
{
"id": "ord-1",
"price": "123",
"name": "order-1",
"date": "23/1/2023"
},
{
"id": "ord-2",
"price": "10",
"name": "order-2",
"date": "23/1/2023"
}
]
},
{
"_id": "63c123ebea7d6123123123536c857e775f93",
"__STATE__": "PUBLIC",
"updatedAt": "2023-01-23T17:06:47.859Z",
"registry_id": "reg-2",
"name": "customer 2",
"city": null,
"state": null,
"orders": [
{
"id": "ord-3",
"price": "25",
"name": "order-3",
"date": "23/1/2023"
}
]
}
]