# Configure `config.json` for DMO-to-DMO Transforms

For data model object (DMO)-to-DMO transforms (transforms that read from and write to DMOs), don't use the **`sf data-code-extension script scan`** command because you must pass the DMO schema. Instead, follow the schema standards to configure the `config.json` file manually.

## Schema Structure Overview

This schema example demonstrates the structure of a `config.json` file for DMO-to-DMO transforms:

```json
{
  "entryPoint": "entry_point.py", // Root-level: Entry point file
  "dataspace": "default", // Root-level: Dataspace name
  "permissions": {
    // Root-level: Permissions object
    "read": {
      // Permissions: Read permissions
      "dmo": ["SourceAccount__dlm"] // Permissions: Input DMO array
    },
    "write": {
      // Permissions: Write permissions
      "dmo": ["EnrichedAccount__dlm"] // Permissions: Output DMO array
    }
  },
  "dataObjects": [
    // Root-level: Data objects array
    {
      // dataObjects: Object definition
      "name": "EnrichedAccount__dlm", // Object: DMO name (required)
      "label": "Enriched Account", // Object: Human-readable label (optional)
      "type": "dataModelObject", // Object: Object type (optional)
      "category": "profile", // Object: Category (required)
      "fields": [
        // Object: Fields array (required)
        {
          // Field: Field definition
          "name": "Id__c", // Field: Field name (required)
          "label": "Account ID", // Field: Human-readable label (optional)
          "dataType": "text", // Field: Data type (required)
          "isPrimaryKey": true, // Field: Primary key flag (required)
          "keyQualifierFieldName": "Name__c" // Field: Key qualifier (optional)
        }
      ]
    }
  ]
}
```

### Root-Level Properties

Your `config.json` file must include these root-level properties.

| Property      | Required | Description                                                                            |
| ------------- | -------- | -------------------------------------------------------------------------------------- |
| `entryPoint`  | Yes      | The main script file that is executed when your code extension runs.                   |
| `dataspace`   | Yes      | The data space that contains the DMOs to read from and write to (typically "default"). |
| `permissions` | Yes      | Read and write permissions for data objects.                                           |
| `dataObjects` | Yes      | Schema definitions for each DMO your script writes to.                                 |

### Permissions Schema

The `permissions` object defines which DMOs your script reads from and writes to. It must follow this schema structure.

```json
"permissions": {
  "read": {
    "dmo": ["SourceDMO__dlm"]
  },
  "write": {
    "dmo": ["TargetDMO__dlm"]
  }
}
```

**Schema Requirements:**

- Include both `read` and `write` permissions.
- Use `dmo` for DMOs.
- Use different object names for read and write operations.
- Every DMO in the write list must have a corresponding schema definition in `dataObjects`. You don't have to define schemas for DMOs in the read list.

### dataObjects Schema

The `dataObjects` array contains schema definitions for each output DMO that your script writes to. Each object in the array defines the complete structure of a DMO, including its fields, data types, and primary keys.

#### Object-Level Schema

Each object in the `dataObjects` array represents one DMO and must include these properties.

| Property         | Required    | Type   | Description                                                                                                         |
| ---------------- | ----------- | ------ | ------------------------------------------------------------------------------------------------------------------- |
| `name`           | Yes         | String | DMO name. Must match a name in `permissions.write.dmo`.                                                             |
| `category`       | Yes         | String | Set to "profile", "engagement", or "other" (case-insensitive).                                                      |
| `fields`         | Yes         | Array  | Array of field definitions. Include at least one field.                                                             |
| `timeEventField` | Conditional | String | Required when `category` is "engagement". Name of a `dateTime` field defined in `fields` that holds the event time. |
| `label`          | No          | String | Human-readable label.                                                                                               |
| `type`           | No          | String | The object type for the schema entry. Set to "dataModelObject" for DMOs.                                            |
| `description`    | No          | String | Description of the DMO.                                                                                             |

##### Supported DMO Categories

You can write to DMOs in any of these categories.

- `profile` - Profile data, such as accounts or contacts.
- `engagement` - Time-series engagement data, such as events or interactions. An engagement DMO must include a `timeEventField` at the object level that names a `dateTime` field in `fields`.
- `other` - Data that doesn't fit the profile or engagement categories.

For an engagement DMO, supply the `timeEventField` property at the object level and define the referenced field as a `dateTime` field in the `fields` array.

```json
{
  "name": "testing_engagement__dlm",
  "label": "Testing Engagement",
  "type": "dataModelObject",
  "category": "engagement",
  "timeEventField": "EventTime__c",
  "fields": [
    {
      "name": "Id__c",
      "label": "Account Id",
      "dataType": "text",
      "isPrimaryKey": true,
      "keyQualifierFieldName": "KQ_Id1__c"
    },
    {
      "name": "KQ_Id1__c",
      "label": "Key Qualifier Account Id",
      "dataType": "text",
      "isPrimaryKey": false,
      "keyQualifierFieldName": null
    },
    {
      "name": "Description__c",
      "label": "Account Description",
      "dataType": "text",
      "isPrimaryKey": false,
      "keyQualifierFieldName": null
    },
    {
      "name": "EventTime__c",
      "label": "Event Time",
      "dataType": "dateTime",
      "isPrimaryKey": false,
      "keyQualifierFieldName": null
    }
  ]
}
```

#### Field-Level Schema

Each field in the `fields` array defines a property of the DMO. Each field must include these properties.

| Property                | Required | Type    | Description                         |
| ----------------------- | -------- | ------- | ----------------------------------- |
| `name`                  | Yes      | String  | Field name. Can't be blank.         |
| `dataType`              | Yes      | String  | Data type. Can't be blank.          |
| `isPrimaryKey`          | Yes      | Boolean | Whether this field is a primary key |
| `label`                 | No       | String  | Human-readable label                |
| `keyQualifierFieldName` | No       | String  | Name of the key qualifier field     |

#### Supported Data Types

Use these data type values in the `dataType` property for your DMO fields.

- `text` - Text or string values
- `email` - Email addresses
- `dateTime` - Date and time values
- `number` - Numeric values
- `boolean` - True or false values

#### Primary Keys and Key Qualifiers

When defining primary keys for your DMO, use `keyQualifierFieldName` to reference another field that provides more context for the primary key.

```json
{
  "name": "Id__c",
  "dataType": "text",
  "isPrimaryKey": true,
  "keyQualifierFieldName": "Name__c"
},
{
  "name": "Name__c",
  "dataType": "text",
  "isPrimaryKey": false,
  "keyQualifierFieldName": null
}
```

#### Multiple DMOs

If your script writes to multiple DMOs, define a schema for each one in the `dataObjects` array. Each DMO must have its own object definition with a complete field schema.

```json
{
  "permissions": {
    "write": {
      "dmo": ["Accounts__dlm", "Contacts__dlm"]
    }
  },
  "dataObjects": [
    {
      "name": "Accounts__dlm",
      "category": "profile",
      "fields": [
        /* ... */
      ]
    },
    {
      "name": "Contacts__dlm",
      "category": "profile",
      "fields": [
        /* ... */
      ]
    }
  ]
}
```

## `config.json` Example

This example shows a complete `config.json` file that follows all the required schema standards for a DMO-to-DMO transform.

```json
{
  "entryPoint": "entry_point.py",
  "dataspace": "default",
  "permissions": {
    "read": {
      "dmo": ["SourceAccount__dlm"]
    },
    "write": {
      "dmo": ["EnrichedAccount__dlm"]
    }
  },
  "dataObjects": [
    {
      "name": "EnrichedAccount__dlm",
      "label": "Enriched Account",
      "type": "dataModelObject",
      "category": "profile",
      "fields": [
        {
          "name": "Id__c",
          "label": "Account ID",
          "dataType": "text",
          "isPrimaryKey": true,
          "keyQualifierFieldName": "Name__c"
        },
        {
          "name": "Name__c",
          "label": "Account Name",
          "dataType": "text",
          "isPrimaryKey": false,
          "keyQualifierFieldName": null
        },
        {
          "name": "Email__c",
          "label": "Email",
          "dataType": "email",
          "isPrimaryKey": false,
          "keyQualifierFieldName": null
        },
        {
          "name": "LastModified__c",
          "label": "Last Modified Date",
          "dataType": "dateTime",
          "isPrimaryKey": false,
          "keyQualifierFieldName": null
        }
      ]
    }
  ]
}
```

## Schema Validation Requirements

Your `config.json` file must meet these validation requirements.

### Configuration Level

- Read and write permissions must not be empty.
- Dataspace must not be blank.
- Don't read and write the same object.

### Schema Level

- `dataObjects` must be present and not empty.
- Every DMO in `permissions.write.dmo` must have a matching schema in `dataObjects`.
- Each object's `name` must not be blank.
- Each object's `category` must be "profile", "engagement", or "other".
- Each object with the "engagement" category must include a `timeEventField` that names a `dateTime` field defined in `fields`.
- Each object must have at least one field.

### Field Level

- Each field's `name` must not be blank.
- Each field's `dataType` must not be blank.
- `isPrimaryKey` must be specified for each field.

## Troubleshoot Common Errors

These errors occur during package validation when your `config.json` doesn't follow the required schema standards.

| Error Message                                | Solution                                                                   |
| -------------------------------------------- | -------------------------------------------------------------------------- |
| No DMO schema found                          | Add `dataObjects` array with schemas for all write DMOs                    |
| Some DMO write targets do not have a schema  | Ensure all DMO names in `write.dmo` have matching entries in `dataObjects` |
| Invalid DMO Schema: name missing             | Add `name` property to data object                                         |
| Invalid DMO Schema: invalid object category  | Set `category` to "profile", "engagement", or "other"                      |
| Invalid DMO fields schema                    | Ensure all fields have `name` and `dataType`                               |
| Can't read and write from/to the same entity | Use different DMO names for read vs write                                  |

:::note
Currently, all these validation errors surface to the Connect API as error code `2999` ("Unknown error occurred"). The preceding error messages are available in the logs for debugging.
:::

## Best Practices

1. **Start with write targets**: List all DMOs you write to in `permissions.write.dmo`.
2. **Create matching schemas**: Make sure that each DMO has a corresponding entry in `dataObjects` with the same name.
3. **Define all output fields**: Include all fields that your script writes.
4. **Set primary keys correctly**: Identify primary keys and mark them with `isPrimaryKey: true`.
5. **Match your script output**: Ensure field names match what your Python script produces.
6. **Use descriptive labels**: While optional, labels improve readability and debugging.
