

# AWS IoT TwinMaker data connectors
Data connectors

AWS IoT TwinMaker uses a connector-based architecture so that you can connect data from your own data store to AWS IoT TwinMaker. This means you don't need to migrate data prior to using AWS IoT TwinMaker. Currently, AWS IoT TwinMaker supports first-party connectors for AWS IoT SiteWise. If you store modeling and property data in AWS IoT SiteWise, then you don’t need to implement your own connectors. If you store your modeling or property data in other data stores, such as Timestream, DynamoDB, or Snowflake, then you must implement AWS Lambda connectors with the AWS IoT TwinMaker data connector interface so that AWS IoT TwinMaker can invoke your connector when necessary. 

**Topics**
+ [

# AWS IoT TwinMaker data connectors
](data-connector-interfaces.md)
+ [

# AWS IoT TwinMaker Athena tabular data connector
](athena-tabular-data-connector.md)
+ [

# Developing AWS IoT TwinMaker time-series data connectors
](time-series-data-connectors.md)

# AWS IoT TwinMaker data connectors
Data connectors

Connectors need access to your underlying data store to resolve sent queries and to return either results or an error.

To learn about the available connectors, their request interfaces, and their response interfaces, see the following topics.

For more information about the properties used in the connector interfaces, see the [GetPropertyValueHistory](https://docs.aws.amazon.com/iot-twinmaker/latest/apireference/API_GetPropertyValueHistory.html) API action.

**Note**  
Some connectors have two timestamp fields in both the request and response interfaces for start time and end time properties. Both `startDateTime` and `endDateTime` use a long number to represent epoch second, which is no longer supported. To maintain backwards-compatibility, we still send a timestamp value to that field, but we recommend using the `startTime` and `endTime` fields that are consistent with our API timestamp format.

**Topics**
+ [

## Schema initializer connector
](#SchemaInitializer-connector)
+ [

## DataReaderByEntity
](#DataReaderByEntity-connector)
+ [

## DataReaderByComponentType
](#DataReaderByComponentType-connector)
+ [

## DataReader
](#DataReader-connector)
+ [

## AttributePropertyValueReaderByEntity
](#AttributePropertyValueReaderByEntity-connector)
+ [

## DataWriter
](#DataWriter-connector)
+ [

## Examples
](#examples-connector)

## Schema initializer connector


You can use the schema initializer in the component type or entity lifecycle to fetch the component type or component properties from the underlying data source. The schema initializer automatically imports component type or component properties without explicitly calling an API action to set up `properties`.

### SchemaInitializer request interface


```
{
  "workspaceId": "string",
  "entityId": "string",
  "componentName": "string",
  "properties": {
    // property name as key,
    // value is of type PropertyRequest 
    "string": "PropertyRequest"
  }
}
```

**Note**  
The map of properties in this request interface is a `PropertyRequest`. For more information, see [PropertyRequest](https://docs.aws.amazon.com//iot-twinmaker/latest/apireference/API_PropertyRequest.html).

### SchemaInitializer response interface


```
{
  "properties": {
    // property name as key,
    // value is of type PropertyResponse 
    "string": "PropertyResponse"
  }
}
```

**Note**  
The map of properties in this request interface is a `PropertyResponse`. For more information, see [PropertyResponse](https://docs.aws.amazon.com//iot-twinmaker/latest/apireference/API_PropertyResponse.html).

## DataReaderByEntity


DataReaderByEntity is a data plane connector that's used to get the time-series values of properties in a single component.

For information about the property types, syntax, and format of this connector, see the [GetPropertyValueHistory](https://docs.aws.amazon.com/iot-twinmaker/latest/apireference/API_GetPropertyValueHistory.html) API action.

### DataReaderByEntity request interface


```
{
  "startDateTime": long, // In epoch sec, deprecated
  "startTime": "string", // ISO-8601 timestamp format
  "endDateTime": long, // In epoch sec, deprecated
  "endTime": "string", // ISO-8601 timestamp format
  "properties": { 
    // A map of properties as in the get-entity API response
    // property name as key,
    // value is of type [PropertyResponse](https://docs.aws.amazon.com//iot-twinmaker/latest/apireference/API_PropertyResponse.html) 
    "string": "PropertyResponse"
   }, 
  "workspaceId": "string",
  "selectedProperties": List:"string",
  "propertyFilters": List:PropertyFilter,
  "entityId": "string",
  "componentName": "string",
  "componentTypeId": "string",
  "interpolation": InterpolationParameters,
  "nextToken": "string",
  "maxResults": int,
  "orderByTime": "string"
  }
```

### DataReaderByEntity response interface


```
{
  "propertyValues": [
    {
      "entityPropertyReference": EntityPropertyReference, // The same as [EntityPropertyReference](https://docs.aws.amazon.com//iot-twinmaker/latest/apireference/API_EntityPropertyReference.html)
      "values": [
        {
        "timestamp": long, // Epoch sec, deprecated
        "time": "string", // ISO-8601 timestamp format
        "value": DataValue // The same as [DataValue](https://docs.aws.amazon.com//iot-twinmaker/latest/apireference/API_DataValue.html)
        }
      ]  
    }
  ],
  "nextToken": "string"
}
```

## DataReaderByComponentType


To get the time-series values of common properties that come from the same component type, use the data plane connector DataReaderByEntity. For example, if you define time-series properties in the component type and you have multiple components using that component type, then you can query those properties across all components in a given a time range. A common use case for this is when you want to query the alarm status of multiple components for a global view of your entities.

For information about the property types, syntax, and format of this connector, see the [GetPropertyValueHistory](https://docs.aws.amazon.com/iot-twinmaker/latest/apireference/API_GetPropertyValueHistory.html) API action.

### DataReaderByComponentType request interface


```
{
  "startDateTime": long, // In epoch sec, deprecated
  "startTime": "string", // ISO-8601 timestamp format
  "endDateTime": long, // In epoch sec, deprecated
  "endTime": "string", // ISO-8601 timestamp format
  "properties": { // A map of properties as in the get-entity API response
    // property name as key,
    // value is of type [PropertyResponse](https://docs.aws.amazon.com//iot-twinmaker/latest/apireference/API_PropertyResponse.html) 
    "string": "PropertyResponse"
   },  
  "workspaceId": "string",
  "selectedProperties": List:"string",
  "propertyFilters": List:PropertyFilter,
  "componentTypeId": "string",
  "interpolation": InterpolationParameters,
  "nextToken": "string",
  "maxResults": int,
  "orderByTime": "string" 
}
```

### DataReaderByComponentType response interface


```
{
  "propertyValues": [
    {
      "entityPropertyReference": EntityPropertyReference, // The same as [EntityPropertyReference](https://docs.aws.amazon.com//iot-twinmaker/latest/apireference/API_EntityPropertyReference.html)
      "entityId": "string",
      "componentName": "string",
      "values": [
        {
        "timestamp": long, // Epoch sec, deprecated
        "time": "string", // ISO-8601 timestamp format
        "value": DataValue // The same as [DataValue](https://docs.aws.amazon.com//iot-twinmaker/latest/apireference/API_DataValue.html)
        }
      ]  
    }
  ],
  "nextToken": "string"
}
```

## DataReader


DataReader is a data plane connector that can handle both the case of DataReaderByEntity and DataReaderByComponentType.

For information about the property types, syntax, and format of this connector, see the [GetPropertyValueHistory](https://docs.aws.amazon.com/iot-twinmaker/latest/apireference/API_GetPropertyValueHistory.html) API action.

### DataReader request interface


 The `EntityId` and `componentName` are optional.

```
{
  "startDateTime": long, // In epoch sec, deprecated
  "startTime": "string", // ISO-8601 timestamp format
  "endDateTime": long, // In epoch sec, deprecated  
  "endTime": "string", // ISO-8601 timestamp format
  "properties": { // A map of properties as in the get-entity API response
    // property name as key,
    // value is of type PropertyRequest 
    "string": "PropertyRequest"
  },
  
  "workspaceId": "string",
  "selectedProperties": List:"string",
  "propertyFilters": List:PropertyFilter,
  "entityId": "string",
  "componentName": "string",
  "componentTypeId": "string",
  "interpolation": InterpolationParameters,
  "nextToken": "string",
  "maxResults": int,
  "orderByTime": "string"
}
```

### DataReader response interface


```
{
  "propertyValues": [
    {
      "entityPropertyReference": EntityPropertyReference, // The same as [EntityPropertyReference](https://docs.aws.amazon.com//iot-twinmaker/latest/apireference/API_EntityPropertyReference.html)
      "values": [
        {
        "timestamp": long, // Epoch sec, deprecated
        "time": "string", // ISO-8601 timestamp format
        "value": DataValue // The same as [DataValue](https://docs.aws.amazon.com//iot-twinmaker/latest/apireference/API_DataValue.html)
        }
      ]  
    }
  ],
  "nextToken": "string"
}
```

## AttributePropertyValueReaderByEntity


AttributePropertyValueReaderByEntity is a data plane connector that you can use to fetch the value of static properties in a single entity.

For information about the property types, syntax, and format of this connector, see the [ GetPropertyValue](https://docs.aws.amazon.com/iot-twinmaker/latest/apireference/API_GetPropertyValue.html) API action.

### AttributePropertyValueReaderByEntity request interface


```
{
  "properties": {
    // property name as key,
    // value is of type [PropertyResponse](https://docs.aws.amazon.com//iot-twinmaker/latest/apireference/API_PropertyResponse.html) 
    "string": "PropertyResponse"
  }
  
  "workspaceId": "string",
  "entityId": "string",
  "componentName": "string",
  "selectedProperties": List:"string",
}
```

### AttributePropertyValueReaderByEntity response interface


```
{
  "propertyValues": {
    "string": { // property name as key
        "propertyReference": EntityPropertyReference, // The same as [EntityPropertyReference](https://docs.aws.amazon.com//iot-twinmaker/latest/apireference/API_EntityPropertyReference.html)
        "propertyValue": DataValue // The same as [DataValue](https://docs.aws.amazon.com//iot-twinmaker/latest/apireference/API_DataValue.html)
    }
}
```

## DataWriter


DataWriter is a data plane connector that you can use to write time-series data points back to the underlying data store for properties in a single component.

For information about the property types, syntax, and format of this connector, see the [BatchPutPropertyValues](https://docs.aws.amazon.com/iot-twinmaker/latest/apireference/API_BatchPutPropertyValues.html) API action.

### DataWriter request interface


```
{
  "workspaceId": "string",
  "properties": {
    // entity id as key
    "String": {
      // property name as key,
      // value is of type [PropertyResponse](https://docs.aws.amazon.com//iot-twinmaker/latest/apireference/API_PropertyResponse.html)      
      "string": PropertyResponse
    } 
  },
  "entries": [
    {
      "entryId": "string",
      "entityPropertyReference": EntityPropertyReference, // The same as [EntityPropertyReference](https://docs.aws.amazon.com//iot-twinmaker/latest/apireference/API_EntityPropertyReference.html)
      "propertyValues": [
        {
        "timestamp": long, // Epoch sec, deprecated
        "time": "string", // ISO-8601 timestamp format
        "value": DataValue // The same as [DataValue](https://docs.aws.amazon.com//iot-twinmaker/latest/apireference/API_DataValue.html)
        }
      ]
    }
  ]
}
```

### DataWriter response interface


```
{
  "errorEntries": [
    {
      "errors": List:BatchPutPropertyError // The value is a list of type [BatchPutPropertyError](https://docs.aws.amazon.com//iot-twinmaker/latest/apireference/API_BatchPutPropertyError.html)
    } 
  ]
}
```

## Examples


The following JSON samples are examples of response and request syntax for multiple connectors.
+ **SchemaInitializer**:

  The following examples show the schema initializer in a component type lifecycle.

  **Request**:

  ```
  {
    "workspaceId": "myWorkspace",
    "properties": {
      "modelId": {
        "definition": {
            "dataType": { "type": "STRING" },
            "isExternalId": true,
            "isFinal": true,
            "isImported": false,
            "isInherited": false,
            "isRequiredInEntity": true,
            "isStoredExternally": false,
            "isTimeSeries": false,
            "defaultValue": {
                "stringValue": "myModelId"
            }
        },
        "value": {
            "stringValue": "myModelId"
        }
      },
      "tableName": {
        "definition": {
            "dataType": { "type": "STRING" },
            "isExternalId": false,
            "isFinal": false,
            "isImported": false,
            "isInherited": false,
            "isRequiredInEntity": false,
            "isStoredExternally": false,
            "isTimeSeries": false,
            "defaultValue": {
                "stringValue": "myTableName"
            }
        },
        "value": {
            "stringValue": "myTableName"
        }
      }
    }
  }
  ```

  **Response**:

  ```
  {
    "properties": {
      "myProperty1": {
        "definition": {
          "dataType": {
            "type": "DOUBLE",
            "unitOfMeasure": "%"
          },
          "configuration": {
            "myProperty1Id": "idValue"
          },
          "isTimeSeries": true
        }
      },
      "myProperty2": {
        "definition": {
          "dataType": { "type": "STRING" },
          "isTimeSeries": false,
          "defaultValue": {
            "stringValue": "property2Value"
          }
        }
      }
    }
  }
  ```
+ **Schema initializer in entity lifecycle**:

  **Request**:

  ```
  {
    "workspaceId": "myWorkspace",
    "entityId": "myEntity",
    "componentName": "myComponent",
    "properties": {
      "assetId": {
        "definition": {
            "dataType": { "type": "STRING" },
            "isExternalId": true,
            "isFinal": true,
            "isImported": false,
            "isInherited": false,
            "isRequiredInEntity": true,
            "isStoredExternally": false,
            "isTimeSeries": false
        },
        "value": {
            "stringValue": "myAssetId"
        }
      },
      "tableName": {
        "definition": {
            "dataType": { "type": "STRING" },
            "isExternalId": false,
            "isFinal": false,
            "isImported": false,
            "isInherited": false,
            "isRequiredInEntity": false,
            "isStoredExternally": false,
            "isTimeSeries": false
        },
        "value": {
            "stringValue": "myTableName"
        }
      }
    }
  }
  ```

  **Response**:

  ```
  {
    "properties": {
      "myProperty1": {
        "definition": {
          "dataType": {
            "type": "DOUBLE",
            "unitOfMeasure": "%"
          },
          "configuration": {
            "myProperty1Id": "idValue"
          },
          "isTimeSeries": true
        }
      },
      "myProperty2": {
        "definition": {
          "dataType": { "type": "STRING" },
          "isTimeSeries": false
        },
        "value": {
          "stringValue": "property2Value"
        }
      }
    }
  }
  ```
+ **DataReaderByEntity and DataReader**:

  **Request**:

  ```
  {
    "workspaceId": "myWorkspace",
    "entityId": "myEntity",
    "componentName": "myComponent",
    "selectedProperties": [
      "Temperature",
      "Pressure"
    ],
    "startTime": "2022-04-07T04:04:42Z",
    "endTime": "2022-04-07T04:04:45Z",
    "maxResults": 4,
    "orderByTime": "ASCENDING",
    "properties": {
        "assetId": {
            "definition": {
                "dataType": { "type": "STRING" },
                "isExternalId": true,
                "isFinal": true,
                "isImported": false,
                "isInherited": false,
                "isRequiredInEntity": true,
                "isStoredExternally": false,
                "isTimeSeries": false
            },
            "value": {
                "stringValue": "myAssetId"
            }
        },
        "Temperature": {
            "definition": {
                "configuration": {
                    "temperatureId": "xyz123"
                },
                "dataType": {
                    "type": "DOUBLE",
                    "unitOfMeasure": "DEGC"
                },
                "isExternalId": false,
                "isFinal": false,
                "isImported": true,
                "isInherited": false,
                "isRequiredInEntity": false,
                "isStoredExternally": false,
                "isTimeSeries": true
            }
        },
        "Pressure": {
            "definition": {
                "configuration": {
                    "pressureId": "xyz456"
                },
                "dataType": {
                    "type": "DOUBLE",
                    "unitOfMeasure": "MPA"
                },
                "isExternalId": false,
                "isFinal": false,
                "isImported": true,
                "isInherited": false,
                "isRequiredInEntity": false,
                "isStoredExternally": false,
                "isTimeSeries": true
            }
        }
    }
  }
  ```

  **Response**:

  ```
  {
    "propertyValues": [
      {
        "entityPropertyReference": {
          "entityId": "myEntity",
          "componentName": "myComponent",
          "propertyName": "Temperature"
        },
        "values": [
          {
            "time": "2022-04-07T04:04:42Z",
            "value": {
              "doubleValue": 588.168
            }
          },
          {
            "time": "2022-04-07T04:04:43Z",
            "value": {
              "doubleValue": 592.4224
            }
          }
        ]
      }
    ],
    "nextToken": "qwertyuiop"
  }
  ```
+ **AttributePropertyValueReaderByEntity**:

  **Request**:

  ```
  {
    "workspaceId": "myWorkspace",
    "entityId": "myEntity",
    "componentName": "myComponent",
    "selectedProperties": [
      "manufacturer",
    ],
    "properties": {
      "assetId": {
        "definition": {
          "dataType": { "type": "STRING" },
          "isExternalId": true,
          "isFinal": true,
          "isImported": false,
          "isInherited": false,
          "isRequiredInEntity": true,
          "isStoredExternally": false,
          "isTimeSeries": false
        },
        "value": {
            "stringValue": "myAssetId"
        }
      },
      "manufacturer": {
        "definition": {
          "dataType": { "type": "STRING" },
          "configuration": {
              "manufacturerPropId": "M001"
          },
          "isExternalId": false,
          "isFinal": false,
          "isImported": false,
          "isInherited": false,
          "isRequiredInEntity": false,
          "isStoredExternally": true,
          "isTimeSeries": false
        }
      }
    }
  }
  ```

  **Response**:

  ```
  {
    "propertyValues": {
      "manufacturer": {
        "propertyReference": {
          "propertyName": "manufacturer",
          "entityId": "myEntity",
          "componentName": "myComponent"
        },
        "propertyValue": {
          "stringValue": "Amazon"
        }
      }
    }
  }
  ```
+ **DataWriter**:

  **Request**:

  ```
  {
    "workspaceId": "myWorkspaceId",
    "properties": {
      "myEntity": {
        "Temperature": {
            "definition": {
                "configuration": {
                    "temperatureId": "xyz123"
                },
                "dataType": {
                    "type": "DOUBLE",
                    "unitOfMeasure": "DEGC"
                },
                "isExternalId": false,
                "isFinal": false,
                "isImported": true,
                "isInherited": false,
                "isRequiredInEntity": false,
                "isStoredExternally": false,
                "isTimeSeries": true
            }
        }
      }
    },
    "entries": [
      {
        "entryId": "myEntity",
        "entityPropertyReference": {
          "entityId": "myEntity",
          "componentName": "myComponent",
          "propertyName": "Temperature"
        },
        "propertyValues": [
          {
            "timestamp": 1626201120,
            "value": {
              "doubleValue": 95.6958
            }
          },
          {
            "timestamp": 1626201132,
            "value": {
              "doubleValue": 80.6959
            }
          }
        ]
      }
    ]
  }
  ```

  **Response**:

  ```
  {
      "errorEntries": [
          {
              "errors": [
                  {
                      "errorCode": "409",
                      "errorMessage": "Conflict value at same timestamp",
                      "entry": {
                          "entryId": "myEntity",
                          "entityPropertyReference": {
                              "entityId": "myEntity",
                              "componentName": "myComponent",
                              "propertyName": "Temperature"
                          },
                          "propertyValues": [
                              "time": "2022-04-07T04:04:42Z",
                              "value": {
                                  "doubleValue": 95.6958
                              }
                          ]
                      }
                  }
              ]
          }
      ]
  }
  ```

# AWS IoT TwinMaker Athena tabular data connector
Athena tabular data connector

With the Athena tabular data connector, you can access and use your Athena data stores in AWS IoT TwinMaker. You can use your Athena data to build digital twins without an intensive data migration effort. You can either use the prebuilt connector or create a custom Athena connector to access data from your Athena data sources.

## AWS IoT TwinMaker Athena data connector prerequisites


Before you use the Athena tabular data connector, complete the following prerequisites:
+ Create managed Athena tables and their associated Amazon S3 resources. For information on using Athena, see the [Athena documentation](https://docs.aws.amazon.com//athena/latest/ug/what-is.html).
+ Create an AWS IoT TwinMaker workspace. You can create a workspace in the [AWS IoT TwinMaker console](https://console.aws.amazon.com/iottwinmaker/).
+ Update your workspace IAM role with Athena permissions. For more information, see [Modify your workspace IAM role to use the Athena data connector](twinmaker-gs-service-role.md#athena-tabular-data-connector-ws-IAM).
+ Become familiar with AWS IoT TwinMaker's entity-component system and how to create entities. For more information, see [Create your first entity](twinmaker-gs-entity.md).
+ Become familiar with AWS IoT TwinMaker's data connectors. For more information, see [AWS IoT TwinMaker data connectors](data-connector-interface.md).

## Using the Athena data connector


To use the Athena data connector, you must create a component, using the Athena connector as the component type. Then you attach the component to an entity within your scene for use in AWS IoT TwinMaker.

 **Create a component type with the Athena data connector**   
Use this procedure to create an AWS IoT TwinMaker component type with the Athena tabular data connector:  

1. Navigate to the [AWS IoT TwinMaker console](https://console.aws.amazon.com/iottwinmaker/).

1. Open an existing workspace or [create a new one](twinmaker-gs-workspace.md).

1. From the left side navigation menu, choose **Component types**, and select **Create component type** to open the component type creation page.

1. On the **Create component type** page, fill in the **ID** field with an ID that matches your use case.  
![\[The component type information console page with entry fields for the ID, description and base type.\]](http://docs.aws.amazon.com/iot-twinmaker/latest/guide/images/CreateComponentType-ComponentTypeInformation.png)

1. Choose the **Base type**. From the dropdown list, select the Athena tabular data connector which is labeled as **com.amazon.athena.connector**.

1. Configure the component type's **Data source** by choosing Athena resources for the following fields:
   + Choose an **Athena datasource**.
   + Choose an **Athena database**.
   + Choose a **Table name**.
   + Choose a **Athena workGroup**.

1. Once you have selected the Athena resources you want to use as the data source, choose which columns from the table you want to include.

1. Select an **External ID column name**. Select a column from the previous step to serve as the external ID column. The external Id is the id that's used to represent an Athena asset and map it to an AWS IoT TwinMaker entity.  
![\[The Athena Data Connector console page lets you choose your datasource, database, table name, and columns.\]](http://docs.aws.amazon.com/iot-twinmaker/latest/guide/images/CreateComponentType-AthenaDataConnector.png)

1. **(Optional)** Add AWS tags to these resources, so you can group and organize them.

1. Choose **Create component type** to finish creating the component type.

**Create a component with the Athena data connector type and attach it to an entity**   
Use this procedure to create an AWS IoT TwinMaker component with the Athena tabular data connector and attach it to an entity:  
You must have an existing component type that uses the Athena tabular data connector as a data source in order to complete this procedure. See the previous procedure **Create a component type with the Athena data connector** before starting this walkthrough.

1. Navigate to the [AWS IoT TwinMaker console](https://console.aws.amazon.com/iottwinmaker/).

1. Open an existing workspace or [create a new one](twinmaker-gs-workspace.md).

1. From the left side navigation menu choose **Entities**, and select the entity you want to add the component to or create a new entity.

1. [ Create a new entity](https://docs.aws.amazon.com//iot-twinmaker/latest/guide/twinmaker-gs-entity.html).

1.  Next select **Add component.**, fill in the **Component name** field with a name that match your use case.

1. From the **Component type** drop down menu select the **component type ID** that you created in the previous procedure.

1. Enter **Component information**, a **Component Name**, and select the child ComponentType created previously. This is the ComponentType you created with the Athena data connector.

1. In the **Properties** section, enter the **athenaComponentExternalId** for the component.  
![\[The Properties console page lets you add the component's properties.\]](http://docs.aws.amazon.com/iot-twinmaker/latest/guide/images/CreateEntity-Properties.png)

1. Choose **Add component** to finish creating the component.

You have now successfully created a component with the Athena data connector as the component type and attached it to an entity.

## Using the Athena tabular data connector JSON reference


The following example is the full the JSON reference for the Athena tabular data connector. Use this as a resource to create custom data connectors and component types.

```
{
    "componentTypeId": "com.amazon.athena.connector",
    "description": "Athena connector for syncing tabular data",
    "workspaceId":"AmazonOwnedTypesWorkspace",
    "propertyGroups": {
        "tabularPropertyGroup": {
            "groupType": "TABULAR",
            "propertyNames": []
        }
    },
    "propertyDefinitions": {
        "athenaDataSource": {
            "dataType": { "type": "STRING" },
            "isRequiredInEntity": true
        },
        "athenaDatabase": {
            "dataType": { "type": "STRING" },
            "isRequiredInEntity": true
        },
        "athenaTable": {
            "dataType": { "type": "STRING" },
            "isRequiredInEntity": true
        },
        "athenaWorkgroup": {
            "dataType": { "type": "STRING" },
            "isRequiredInEntity": true
        },
        "athenaExternalIdColumnName": {
            "dataType": { "type": "STRING" },
            "isRequiredInEntity": true,
            "isExternalId": false
        },
        "athenaComponentExternalId": {
            "dataType": { "type": "STRING" },
            "isStoredExternally": false,
            "isRequiredInEntity": true,
            "isExternalId": true
        }
    },
    "functions": {
        "tabularDataReaderByEntity": {
            "implementedBy": {
                "isNative": true
            }
        }
    }
}
```

## Using the Athena data connector


You can surface your entities that are using Athena tables in Grafana. For more information, see [AWS IoT TwinMaker Grafana dashboard integration](https://docs.aws.amazon.com/iot-twinmaker/latest/guide/grafana-integration.html).

Read the [Athena documentation](https://docs.aws.amazon.com//athena/latest/ug/what-is.html) for information on creating and using Athena tables to store data.

### Troubleshooting the Athena data connector


This topic covers common issues you may encounter when configuring the Athena data connector.

Athena workgroup location:  
When creating Athena connector componentType, an Athena workgroup has to have output location setup. See [How workgroups work](https://docs.aws.amazon.com//athena/latest/ug/user-created-workgroups.html).

Missing IAM role permissions:  
The AWS IoT TwinMaker; workspace role may be missing Athena API access permission when creating a componentType, adding a Ca component to an entity, or running the GetPropertyValue API. To update IAM permissions see [ Create and manage a service role for AWS IoT TwinMaker](https://docs.aws.amazon.com/iot-twinmaker/latest/guide/twinmaker-gs-service-role.html).  


## Visualize Athena tabular data in Grafana


 A Grafana plugin is also available to visualize your tabular data on Grafana a dashboard panel with additional features such as sorting and filtering based on selected properties without making API calls to AWS IoT TwinMaker, or interactions with Athena. This topic shows you how to configure Grafana to visualize Athena tabular data.

### Prerequisites


Before configuring a Grafana panel for visualizing Athena tabular data, review the following prerequisites:
+ You have set up a Grafana environment. For more information see, [AWS IoT TwinMaker Grafana integration](https://docs.aws.amazon.com/iot-twinmaker/latest/guide/grafana-integration.html).
+ You can configure a Grafana datasource. For more information see, [ Grafana AWS IoT TwinMaker](https://github.com/grafana/grafana-iot-twinmaker-app/blob/main/src/datasource/README.md).
+ You are familiar with creating a new dashboard and add a new panel.

### Visualize Athena tabular data in Grafana


This procedure shows you how to setup a Grafana panel to visualize Athena tabular data.

1. Open your AWS IoT TwinMaker Grafana dashboard.

1. Select the **Table** panel in the panel settings.

1. Select your datasource in the query configuration.

1. Select the **Get Property Value** query.

1. Select an entity.

1. Select a component that has a componentType that extends the **Athena base component type**.

1. Select the property group of your Athena table.

1. Select any number of properties from the property group.

1. Configure the tabular conditions through a list of filters and property orders. With the following options:
   + **Filter**: define an expression for a property value to filter your data.
   + **OrderBy**: specify whether data should be returned in ascending or descending order for a property.  
![\[An example of a Grafana console panel that is set up to visualize Athena tabular data.\]](http://docs.aws.amazon.com/iot-twinmaker/latest/guide/images/ate-grafana-panel.png)

# Developing AWS IoT TwinMaker time-series data connectors
AWS IoT TwinMaker time-series data connector

This section explains how to develop a time-series data connector in a step-by-step process. Additionally, we present an example time-series data connector based of the entire cookie factory sample, which includes 3D models, entities, components, alarms, and connectors. The cookie factory sample source is available on the [AWS IoT TwinMaker samples GitHub repository ]( https://github.com/aws-samples/aws-iot-twinmaker-samples).

**Topics**
+ [

## AWS IoT TwinMaker time-series data connector prerequisites
](#time-series-data-connectors-prereqs)
+ [

## Time-series data connector background
](#time-series-data-connectors-background)
+ [

## Developing a time-series data connector
](#time-series-data-connectors-develop)
+ [

## Improving your data connector
](#time-series-data-connectors-improve)
+ [

## Testing your connector
](#time-series-data-connectors-test)
+ [

## Security
](#time-series-data-connectors-security)
+ [

## Creating AWS IoT TwinMaker resources
](#time-series-data-connectors-resources)
+ [

## What's next
](#time-series-data-connectors-wn)
+ [

# AWS IoT TwinMaker cookie factory example time-series connector
](time-series-data-connectors-example.md)

## AWS IoT TwinMaker time-series data connector prerequisites


Before developing your time-series data connector, we recommend that you complete the following tasks:
+ Create an [AWS IoT TwinMaker workspace](twinmaker-gs-workspace.md).
+ Create [AWS IoT TwinMaker component types](https://docs.aws.amazon.com//iot-twinmaker/latest/guide/twinmaker-component-types.html).
+ Create [AWS IoT TwinMaker entities](https://docs.aws.amazon.com//iot-twinmaker/latest/guide/twinmaker-gs-entity.html).
+ (Optional) Read [ Using and creating component types](https://docs.aws.amazon.com//iot-twinmaker/latest/guide/twinmaker-component-types.htm).
+ (Optional) Read [AWS IoT TwinMaker data connector interface](https://docs.aws.amazon.com//iot-twinmaker/latest/guide/data-connector-interface.html) to get a general understanding of AWS IoT TwinMaker data connectors.

**Note**  
For an example of a fully implemented connector, see our cookie factory example implementation.

## Time-series data connector background


Imagine you are working with a factory that has a set of cookie mixers and a water tank. You would like to build AWS IoT TwinMaker digital twins of these physical entities so that you can monitor their operational states by checking various time-series metrics.

You have on-site sensors set up and you are already streaming measurement data into a Timestream database. You want to be able to view and organize the measurement data in AWS IoT TwinMaker with minimal overhead. You can accomplish this task by using a time-series data connector. The following image shows an example telemetry table, which is populated through the use of a time-series connector.

![\[An example of telemetry table data that includes the Asset ID, Type, measure, time, and values.\]](http://docs.aws.amazon.com/iot-twinmaker/latest/guide/images/image(1).png)


The datasets and the Timestream table used in this screenshot are available in the [AWS IoT TwinMaker samples GitHub repository](https://github.com/aws-samples/aws-iot-twinmaker-samples). Also see the [cookie factory example connector](time-series-data-connectors-example.md) for the implementation, which produces the result shown in the preceding screenshot.

### Time-series data connector data flow


For data plane queries, AWS IoT TwinMaker fetches the corresponding properties of both components and component types from components and component types definitions. AWS IoT TwinMaker forwards properties to AWS Lambda functions along with any API query parameters in the query.

AWS IoT TwinMaker uses Lambda functions to access and resolve queries from data sources and return the results of those queries. The Lambda functions use the component and component type properties from the data plane to resolve the initial request.

The results of the Lambda query are mapped to an API response and returned to you.

AWS IoT TwinMaker defines the data connector interface and uses that to interact with Lambda functions. Using data connectors, you can query your data source from AWS IoT TwinMaker API without any data migration efforts. The following image outlines the basic data flow described in the previous paragraphs.

![\[API requests and responses use 3P Connector requests and responses that access a data source.\]](http://docs.aws.amazon.com/iot-twinmaker/latest/guide/images/data_flow.drawio.png)


## Developing a time-series data connector


The following procedure outlines a development model that incrementally builds up to a functional time-series data connector. The basic steps are as follows:

1. **Create a valid basic component type**

   In a component type, you define common properties that are shared across your components. To learn more about defining component types, see [Using and creating component types](https://docs.aws.amazon.com//iot-twinmaker/latest/guide/twinmaker-component-types.html).

   AWS IoT TwinMaker uses an [entity-component modeling pattern](https://en.wikipedia.org/wiki/Entity_component_system) so each component is attached to an entity. We recommend that you model each physical item as an entity and model different data sources with their own component types.

   The following example shows a Timestream template component type with one property:

   ```
   {"componentTypeId": "com.example.timestream-telemetry",
       "workspaceId": "MyWorkspace",
       "functions": {
           "dataReader": {
               "implementedBy": {
                   "lambda": {
                       "arn": "lambdaArn"
                   }
               }
           }
       },
       "propertyDefinitions": {
           "telemetryType": {
               "dataType": { "type": "STRING" },
               "isExternalId": false,
               "isStoredExternally": false,
               "isTimeSeries": false,
               "isRequiredInEntity": true
           },
           "telemetryId": {
               "dataType": { "type": "STRING" },
               "isExternalId": true,
               "isStoredExternally": false,
               "isTimeSeries": false,
               "isRequiredInEntity": true
           },
           "Temperature": {
               "dataType": { "type": "DOUBLE" },
               "isExternalId": false,
               "isTimeSeries": true,
               "isStoredExternally": true,
               "isRequiredInEntity": false
           }
       }
   }
   ```

   The key elements of the component type are the following:
   + The `telemetryId` property identifies the unique key of the physical item in the corresponding data source. The data connector uses this property as a filter condition to only query values associated with the given item. Additionally, if you include the `telemetryId` property value in the data plane API response, then the client side takes the ID and can perform a reverse lookup if necessary.
   + The `lambdaArn` field identifies the Lambda function with which the component type engages.
   + The `isRequiredInEntity` flag enforces the ID creation. This flag is required so that when the component is created, the item's ID is also instantiated.
   + The `TelemetryId` is added to the component type as an external id so that the item can be identified in the Timestream table.

1. **Create a component with the component type**

   To use the component type you created, you must create a component and attach it to the entity from which you wish to retrieve data. The following steps detail the process of creating that component:

   1. Navigate to the [AWS IoT TwinMaker console](https://console.aws.amazon.com/iottwinmaker/).

   1. Select and open the same workspace in which you created the component types.

   1. Navigate to the entity page.

   1. Create a new entity or select an existing entity from the table.

   1. Once you have selected the entity you wish to use, choose **Add component** to open the **Add component** page.

   1. Give the component a name and for the **Type**, choose the component type you created with the template in **1. Create a valid basic component type**.

1. **Make your component type call a Lambda connector**

   The Lambda connector needs to access the data source and generate the query statement based on the input and forward it to the data source. The following example shows a JSON request template that does this. 

   ```
   {
     "workspaceId": "MyWorkspace",
     "entityId": "MyEntity",
     "componentName": "TelemetryData",
     "selectedProperties": ["Temperature"],
     "startTime": "2022-08-25T00:00:00Z",
     "endTime": "2022-08-25T00:00:05Z",
     "maxResults": 3,
     "orderByTime": "ASCENDING",
     "properties": {
         "telemetryType": {
             "definition": {
                 "dataType": { "type": "STRING" },
                 "isExternalId": false,
                 "isFinal": false,
                 "isImported": false,
                 "isInherited": false,
                 "isRequiredInEntity": false,
                 "isStoredExternally": false,
                 "isTimeSeries": false
             },
             "value": {
                 "stringValue": "Mixer"
             }
         },
         "telemetryId": {
             "definition": {
                 "dataType": { "type": "STRING" },
                 "isExternalId": true,
                 "isFinal": true,
                 "isImported": false,
                 "isInherited": false,
                 "isRequiredInEntity": true,
                 "isStoredExternally": false,
                 "isTimeSeries": false
             },
             "value": {
                 "stringValue": "item_A001"
             }
         },
         "Temperature": {
             "definition": {
                 "dataType": { "type": "DOUBLE", },
                 "isExternalId": false,
                 "isFinal": false,
                 "isImported": true,
                 "isInherited": false,
                 "isRequiredInEntity": false,
                 "isStoredExternally": false,
                 "isTimeSeries": true
             }
         }
     }
   }
   ```

   The key elements of the request:
   + The `selectedProperties` is a list you populate with the properties for which you want Timestream measurements.
   + The `startDateTime`, `startTime`, `EndDateTime`, and `endTime` fields specify a time range for the request. This determines the sample range for the measurements returned. 
   + The `entityId` is the name of the entity from which you are querying data.
   + The `componentName` is the name of the component from which you are querying data.
   + Use the `orderByTime` field to organize the order in which the results are displayed.

   In the preceding example request, we would expect to get a series of samples for the selected properties during the given time window for the given item, with the selected time order. The response statement can be summarized as the following:

   ```
   {
     "propertyValues": [
       {
         "entityPropertyReference": {
           "entityId": "MyEntity",
           "componentName": "TelemetryData",
           "propertyName": "Temperature"
         },
         "values": [
           {
             "time": "2022-08-25T00:00:00Z",
             "value": {
               "doubleValue": 588.168
             }
           },
           {
             "time": "2022-08-25T00:00:01Z",
             "value": {
               "doubleValue": 592.4224
             }
           },
           {
             "time": "2022-08-25T00:00:02Z",
             "value": {
               "doubleValue": 594.9383
             }
           }
         ]
       }
     ],
     "nextToken": "..."
   }
   ```

1. **Update your component type to have two properties**

   The following JSON template shows a valid component type with two properties:

   ```
   {
       "componentTypeId": "com.example.timestream-telemetry",
       "workspaceId": "MyWorkspace",
       "functions": {
           "dataReader": {
               "implementedBy": {
                   "lambda": {
                       "arn": "lambdaArn"
                   }
               }
           }
       },
       "propertyDefinitions": {
           "telemetryType": {
               "dataType": { "type": "STRING" },
               "isExternalId": false,
               "isStoredExternally": false,
               "isTimeSeries": false,
               "isRequiredInEntity": true
           },
           "telemetryId": {
               "dataType": { "type": "STRING" },
               "isExternalId": true,
               "isStoredExternally": false,
               "isTimeSeries": false,
               "isRequiredInEntity": true
           },
           "Temperature": {
               "dataType": { "type": "DOUBLE" },
               "isExternalId": false,
               "isTimeSeries": true,
               "isStoredExternally": true,
               "isRequiredInEntity": false
           },
           "RPM": {
               "dataType": { "type": "DOUBLE" },
               "isExternalId": false,
               "isTimeSeries": true,
               "isStoredExternally": true,
               "isRequiredInEntity": false
           }
       }
   }
   ```

1. **Update the Lambda connector to handle the second property**

   The AWS IoT TwinMaker data plane API supports querying multiple properties in a single request, and AWS IoT TwinMaker follows a single request to a connector by providing a list of `selectedProperties`.

   The following JSON request shows a modified template that now supports a request for two properties.

   ```
   {
     "workspaceId": "MyWorkspace",
     "entityId": "MyEntity",
     "componentName": "TelemetryData",
     "selectedProperties": ["Temperature", "RPM"],
     "startTime": "2022-08-25T00:00:00Z",
     "endTime": "2022-08-25T00:00:05Z",
     "maxResults": 3,
     "orderByTime": "ASCENDING",
     "properties": {
         "telemetryType": {
             "definition": {
                 "dataType": { "type": "STRING" },
                 "isExternalId": false,
                 "isFinal": false,
                 "isImported": false,
                 "isInherited": false,
                 "isRequiredInEntity": false,
                 "isStoredExternally": false,
                 "isTimeSeries": false
             },
             "value": {
                 "stringValue": "Mixer"
             }
         },
         "telemetryId": {
             "definition": {
                 "dataType": { "type": "STRING" },
                 "isExternalId": true,
                 "isFinal": true,
                 "isImported": false,
                 "isInherited": false,
                 "isRequiredInEntity": true,
                 "isStoredExternally": false,
                 "isTimeSeries": false
             },
             "value": {
                 "stringValue": "item_A001"
             }
         },
         "Temperature": {
             "definition": {
                 "dataType": { "type": "DOUBLE" },
                 "isExternalId": false,
                 "isFinal": false,
                 "isImported": true,
                 "isInherited": false,
                 "isRequiredInEntity": false,
                 "isStoredExternally": false,
                 "isTimeSeries": true
             }
         },
         "RPM": {
             "definition": {
                 "dataType": { "type": "DOUBLE" },
                 "isExternalId": false,
                 "isFinal": false,
                 "isImported": true,
                 "isInherited": false,
                 "isRequiredInEntity": false,
                 "isStoredExternally": false,
                 "isTimeSeries": true
             }
         }
     }
   }
   ```

   Similarly, the corresponding response is also updated, as shown in the following example:

   ```
   {
     "propertyValues": [
       {
         "entityPropertyReference": {
           "entityId": "MyEntity",
           "componentName": "TelemetryData",
           "propertyName": "Temperature"
         },
         "values": [
           {
             "time": "2022-08-25T00:00:00Z",
             "value": {
               "doubleValue": 588.168
             }
           },
           {
             "time": "2022-08-25T00:00:01Z",
             "value": {
               "doubleValue": 592.4224
             }
           },
           {
             "time": "2022-08-25T00:00:02Z",
             "value": {
               "doubleValue": 594.9383
             }
           }
         ]
       },
       {
         "entityPropertyReference": {
           "entityId": "MyEntity",
           "componentName": "TelemetryData",
           "propertyName": "RPM"
         },
         "values": [
           {
             "time": "2022-08-25T00:00:00Z",
             "value": {
               "doubleValue": 59
             }
           },
           {
             "time": "2022-08-25T00:00:01Z",
             "value": {
               "doubleValue": 60
             }
           },
           {
             "time": "2022-08-25T00:00:02Z",
             "value": {
               "doubleValue": 60
             }
           }
         ]
       }
     ],
     "nextToken": "..."
   }
   ```
**Note**  
In terms of the pagination for this case, the page size in the request applies to all properties. This means that with five properties in the query and a page size of 100, if there are enough data points in the source, you should expect to see 100 data points per property, with 500 data points in total.

   For an example implementation, see [Snowflake connector sample](https://github.com/aws-samples/aws-iot-twinmaker-samples-snowflake/blob/main/src/modules/snowflake/data-connector/lambda_connectors/data_reader_by_entity.py) on GitHub.

## Improving your data connector




### Handling exceptions


It is safe for the Lambda connector to throw exceptions. In the data plane API call, the AWS IoT TwinMaker service waits for the Lambda function to return a response. If the connector implementation throws an exception, AWS IoT TwinMaker translates the exception type to be `ConnectorFailure`, making the API client aware that an issue happened inside the connector.

### Handling pagination


In the example, Timestream provides a [utility function](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/timestream-query.html#TimestreamQuery.Client.query) which can help support pagination natively. However, for some other query interfaces, such as SQL, it might need extra effort to implement an efficient pagination algorithm. There is a [Snowflake](https://github.com/aws-samples/aws-iot-twinmaker-samples-snowflake/blob/main/src/modules/snowflake/data-connector/lambda_connectors/data_reader_by_entity.py) connector example that handles pagination in an SQL interface.

When the new token is returned to AWS IoT TwinMaker through the connector response interface, the token is encrypted before being returned to the API client. When the token is included in another request, AWS IoT TwinMaker decrypts it before forwarding it to the Lambda connector. We recommend that you avoid adding sensitive information to the token.

## Testing your connector


Though you can still update the implementation after you link the connector to the component type, we strongly recommend you verify the Lambda connector before integrating with AWS IoT TwinMaker.

There are multiple ways to test your Lambda connector: you can test the Lambda connector in the Lambda console or locally in the AWS CDK.

For more information on testing your Lambda functions, see [Testing Lambda functions](https://docs.aws.amazon.com//lambda/latest/dg/testing-functions.html) and [Locally testing AWS CDK applications](https://docs.aws.amazon.com//serverless-application-model/latest/developerguide/serverless-cdk-testing.html).

## Security


For documentation on security best practices with Timestream, see [Security in Timestream](https://docs.aws.amazon.com//timestream/latest/developerguide/security.html).

For an example of SQL injection prevention, see the following [Python script](https://github.com/aws-samples/aws-iot-twinmaker-samples/blob/main/src/libs/udq_helper_utils/udq_utils/sql_detector.py) in AWS IoT TwinMaker Samples GitHub Repository.

## Creating AWS IoT TwinMaker resources


Once you have implemented the Lambda function, you can create AWS IoT TwinMaker resources such as component types, entities, and components through the [AWS IoT TwinMaker console](https://console.aws.amazon.com/iottwinmaker/) or API.

**Note**  
If you follow the setup instructions in the GitHub sample, all AWS IoT TwinMaker resources are available automatically. You can check the component type definitions in the [AWS IoT TwinMaker GitHub sample](https://github.com/aws-samples/aws-iot-twinmaker-samples/tree/main/src/workspaces/cookiefactory/component_types). Once the component type is used by any components, the property definitions and functions of the component type cannot be updated.

### Integration testing


We recommend having an integrated test with AWS IoT TwinMaker to verify the data plane query works end-to-end. You can perform that through [GetPropertyValueHistory](https://docs.aws.amazon.com//iot-twinmaker/latest/apireference/API_GetPropertyValueHistory.html) API or easily in [AWS IoT TwinMaker console](https://console.aws.amazon.com/iottwinmaker/).

![\[A TwinMaker Component information console page shows the component's name, type, status, and so on.\]](http://docs.aws.amazon.com/iot-twinmaker/latest/guide/images/image(3).png)


In the AWS IoT TwinMaker console, go to **component details** and then under the **Test**, you’ll see all the properties in the component are listed there. The **Test** area of the console allows you to test time-series properties as well as non-time-series properties. For time-series properties you can also use the [ GetPropertyValueHistory](https://docs.aws.amazon.com//iot-twinmaker/latest/apireference/API_GetPropertyValueHistory.html) API and for non-time-series properties use [ GetPropertyValue](https://docs.aws.amazon.com//iot-twinmaker/latest/apireference/API_GetPropertyValue.html) API. If your Lambda connector supports multiple property query, you can choose more than one property.

![\[A portion of a TwinMaker Component information console page showing a component's test.\]](http://docs.aws.amazon.com/iot-twinmaker/latest/guide/images/image(4).png)


## What's next


You can now set up an [AWS IoT TwinMaker Grafana dashboard](https://docs.aws.amazon.com//iot-twinmaker/latest/guide/grafana-integration.html) to visualize metrics. You can also explore other data connector samples in the [AWS IoT TwinMaker samples GitHub repository ](https://github.com/aws-samples/aws-iot-twinmaker-samples/tree/main/src/modules/s3) to see if they fit your use case. 

# AWS IoT TwinMaker cookie factory example time-series connector
AWS IoT TwinMaker cookie factory data connector

The complete [code of the cookie factory](https://github.com/aws-samples/aws-iot-twinmaker-samples/blob/main/src/modules/timestream_telemetry/lambda_function/udq_data_reader.py) Lambda function is available on GitHub. Though you can still update the implementation after you link the connector to the component type, we strongly recommend you verify the Lambda connector before integrating with AWS IoT TwinMaker. You can test your Lambda function in the Lambda console or locally in the AWS CDK. For more information on testing your Lambda functions, see [Testing Lambda functions](https://docs.aws.amazon.com//lambda/latest/dg/testing-functions.html), and [Locally testing AWS CDK applications](https://docs.aws.amazon.com//serverless-application-model/latest/developerguide/serverless-cdk-testing.html). 

## Example cookie factory component types


In a component type, we define common properties that are shared across components. For the cookie factory example, physical components of the same type share the same measurements, so we can define the measurements schema in the component type. As an example, the mixer type is defined in the following example.

```
{
    "componentTypeId": "com.example.cookiefactory.mixer"
    "propertyDefinitions": {
        "RPM": {
            "dataType": { "type": "DOUBLE" },
            "isTimeSeries": true,
            "isRequiredInEntity": false,
            "isExternalId": false,
            "isStoredExternally": true
        },
        "Temperature": {
            "dataType": { "type": "DOUBLE" },
            "isTimeSeries": true,
            "isRequiredInEntity": false,
            "isExternalId": false,
            "isStoredExternally": true
        }
    }
}
```

For example, a physical component might have measurements in a Timestream database, maintenance records in an SQL database, or alarm data in alarm systems. Creating multiple components and associating them with an entity links different data sources to the entity and populates the entity-component graph. In this context, each component needs an `telemetryId` property to identify the unique key of the component in the corresponding data source. Specifying the `telemetryId` property has two benefits: the property can be used in the data connector as a filter condition to only query values of the given component and, if you include the `telemetryId` property value in the data plane API response, then the client side takes the ID and can perform a reverse lookup if necessary.

If you add the `TelemetryId` to the component type as an external id, it identifies the component in the `TimeStream` table.

```
{
    "componentTypeId": "com.example.cookiefactory.mixer"
    "propertyDefinitions": {
        "telemetryId": {
            "dataType": { "type": "STRING" },
            "isTimeSeries": false,
            "isRequiredInEntity": true,
            "isExternalId": true,
            "isStoredExternally": false
        },
        "RPM": {
            "dataType": { "type": "DOUBLE" },
            "isTimeSeries": true,
            "isRequiredInEntity": false,
            "isExternalId": false,
            "isStoredExternally": true
        },
        "Temperature": {
            "dataType": { "type": "DOUBLE" },
            "isTimeSeries": true,
            "isRequiredInEntity": false,
            "isExternalId": false,
            "isStoredExternally": true
        }
    }
}
```

Similarly we have the component type for the `WaterTank`, as shown in the following JSON example.

```
{
  "componentTypeId": "com.example.cookiefactory.watertank",
  "propertyDefinitions": {
    "flowRate1": {
      "dataType": { "type": "DOUBLE" },
      "isTimeSeries": true,
      "isRequiredInEntity": false,
      "isExternalId": false,
      "isStoredExternally": true
    },
    "flowrate2": {
      "dataType": { "type": "DOUBLE" },
      "isTimeSeries": true,
      "isRequiredInEntity": false,
      "isExternalId": false,
      "isStoredExternally": true
    },
    "tankVolume1": {
      "dataType": { "type": "DOUBLE" },
      "isTimeSeries": true,
      "isRequiredInEntity": false,
      "isExternalId": false,
      "isStoredExternally": true
    },
    "tankVolume2": {
      "dataType": { "type": "DOUBLE" },
      "isTimeSeries": true,
      "isRequiredInEntity": false,
      "isExternalId": false,
      "isStoredExternally": true
    },
    "telemetryId": {
      "dataType": { "type": "STRING" },
      "isTimeSeries": false,
      "isRequiredInEntity": true,
      "isExternalId": true,
      "isStoredExternally": false
    }
  }
}
```

The `TelemetryType` is an optional property in the component type if it's aimed at querying property values in the entity scope. For an example, see the defined component types in the [AWS IoT TwinMaker samples GitHub repository](https://github.com/aws-samples/aws-iot-twinmaker-samples/tree/main/src/workspaces/cookiefactory/component_types). There are alarm types also embedded into the same table, so the `TelemetryType` is defined and you extract common properties like the `TelemetryId` and `TelemetryType` to a parent component type for other child types to share.

## Example Lambda


The Lambda connector needs to access the data source and generate the query statement based on the input and forward it to the data source. An example request sent to the Lambda is shown in the following JSON example.

```
{
    'workspaceId': 'CookieFactory', 
    'selectedProperties': ['Temperature'], 
    'startDateTime': 1648796400, 
    'startTime': '2022-04-01T07:00:00.000Z', 
    'endDateTime': 1650610799, 
    'endTime': '2022-04-22T06:59:59.000Z', 
    'properties': {
        'telemetryId': {
            'definition': {
                'dataType': { 'type': 'STRING' },
                'isTimeSeries': False, 
                'isRequiredInEntity': True, 
                'isExternalId': True, 
                'isStoredExternally': False, 
                'isImported': False, 
                'isFinal': False, 
                'isInherited': True, 
            }, 
            'value': {
                'stringValue': 'Mixer_22_680b5b8e-1afe-4a77-87ab-834fbe5ba01e'
            }
        }
        'Temperature': {
            'definition': {
                'dataType': { 'type': 'DOUBLE' }, 
                'isTimeSeries': True, 
                'isRequiredInEntity': False, 
                'isExternalId': False, 
                'isStoredExternally': True, 
                'isImported': False, 
                'isFinal': False, 
                'isInherited': False
            }
        }
        'RPM': {
            'definition': {
                'dataType': { 'type': 'DOUBLE' }, 
                'isTimeSeries': True, 
                'isRequiredInEntity': False, 
                'isExternalId': False, 
                'isStoredExternally': True, 
                'isImported': False, 
                'isFinal':False, 
                'isInherited': False
            }
        }, 
    'entityId': 'Mixer_22_d133c9d0-472c-48bb-8f14-54f3890bc0fe', 
    'componentName': 'MixerComponent', 
    'maxResults': 100, 
    'orderByTime': 'ASCENDING'
}
```

The goal of the Lambda function is to query historical measurement data for a given entity. AWS IoT TwinMaker provides a component-properties map, and you should specify an instantiated value for the component ID. For example, to handle the component type-level query (which is common for alarm use cases) and return the alarm status of all components in the workspace, then the properties map has component type properties definitions.

For the most straightforward case, as in the preceding request, we want a series of temperature samples during the given time window for the given component, in ascending time order. The query statement can be summarized as the following:

```
...
SELECT measure_name, time, measure_value::double
    FROM {database_name}.{table_name} 
    WHERE time < from_iso8601_timestamp('{request.start_time}')
    AND time >= from_iso8601_timestamp('{request.end_time}')
    AND TelemetryId = '{telemetry_id}'
    AND measure_name = '{selected_property}'
    ORDER BY time {request.orderByTime}
...
```