# Migration

Source: https://docs.interop.io/manager/capabilities/layouts/migration/index.html

## Overview

Available since io.Manager 4.0

The Layout migration is a one-time operation that migrates the [legacy Layouts](https://docs.interop.io/manager/capabilities/layouts/overview/index.md#legacy_layouts) stored by the **io.Manager** Server to [advanced Layouts](https://docs.interop.io/manager/capabilities/layouts/overview/index.md#advanced_layouts). Run it once after [enabling advanced Layouts](https://docs.interop.io/manager/capabilities/layouts/overview/index.md#advanced_layouts-enabling_advanced_layouts) on a server that already holds legacy Layouts, so that the io.Connect platforms can find their Global and Workspace Layouts in the new model.

The migration is available only while advanced Layouts are enabled - otherwise, the migration endpoints reject requests with an HTTP 400 status code. You can review and run the migration from the ["Migrate legacy layouts"](https://docs.interop.io/manager/capabilities/layouts/admin-ui/index.md#advanced_layouts-migrating_legacy_layouts) page of the **io.Manager** Admin UI or via the [migration endpoints](https://docs.interop.io/manager/capabilities/layouts/overview/index.md#rest_api-layout_migration) of the **io.Manager** Server REST API.

The [`@interopio/manager-api`](https://www.npmjs.com/package/@interopio/manager-api) library exposes the same operations via the `getMigration()`, `getMigrationLayouts()`, and `runMigration()` methods accessible via the `advancedLayouts` property of an `AdminAPI` instance.

> ⚠️ *Note that a legacy Layout is migrated only once. Changes made to a legacy Layout after it has been migrated (e.g., while the server is reverted to using legacy Layouts) aren't carried over to its advanced counterpart by a later run. Changes made to advanced Layouts after the migration (saving, sharing, renaming, or deleting a Layout) affect only the advanced Layouts. Once the migration status is `"completed-clean"`, no legacy Layout can be migrated, including legacy Layouts created after the clean run.*

## Layout Conversion

Each migrated Layout keeps the `id`, `name`, `type`, `definition`, and `disabled` fields of the legacy Layout, as well as the users that created and last modified it and the times of creation and last modification. The [access information](https://docs.interop.io/manager/capabilities/layouts/sharing/index.md) of the migrated Layout is derived from the visibility fields of the legacy Layout:

| Access Level | Owner | Access Entities | Legacy Layout |
|--------------|-------|-----------------|---------------|
| `"private"` | The owner of the legacy Layout. | None. | Owned by a user and not public. |
| `"public"` | The user that created the legacy Layout. | None. | The `public` property is set to `true`. |
| `"shared"` | The user that created the legacy Layout. | Each group in `accessList`, granted `"view"` access. | Common Layout (`owner` is `*` or not set) that isn't public and is shared with one or more groups via `accessList`. |

A legacy Layout that fits none of these rows (e.g., a common Layout shared with no groups, or a public Layout with no recorded creator) isn't migrated and is reported as a failure.

The migration doesn't apply the [sharing restriction](https://docs.interop.io/manager/capabilities/layouts/sharing/index.md#restricting_sharing) - a legacy Layout that becomes `"shared"` or `"public"` is migrated even if its new owner isn't allowed to share Layouts.

### Default Global Layouts

A legacy Layout can be marked as `default` while being public or shared with groups, which designates the same default Global Layout for several users. Advanced Layouts have no default flag - each user has their own default Global Layout. The migration carries the default designation over as follows:

- An enabled private Global Layout marked as `default` becomes the default Global Layout of its owner, unless the owner already has a default advanced Layout, in which case the existing default is kept. When a user owns several such Layouts, the most recently modified one becomes the default Global Layout of that user. A default whose owner isn't a known user is dropped without being reported.
- Any other Layout marked as `default` is migrated, but loses its default designation.

The [migration status](#previewing) reports how many defaults are eligible to be carried over and how many will be dropped, so that you can review them before running the migration. The outcome of the run reports how many defaults were actually assigned.

## Previewing

Before running the migration, review the changes it will make on the ["Migrate legacy layouts"](https://docs.interop.io/manager/capabilities/layouts/admin-ui/index.md#advanced_layouts-migrating_legacy_layouts) page of the Admin UI or via the [migration endpoints](https://docs.interop.io/manager/capabilities/layouts/overview/index.md#rest_api-layout_migration). The `GET /advancedLayouts/migration/status` endpoint reports the following:

| Field | Description |
|-------|-------------|
| `carriedDefaultsCount` | The number of default Global Layouts among the Layouts to be migrated that are eligible to become the default Global Layout of their owner. The `defaultsAssignedCount` field of the run outcome reports how many were actually assigned. |
| `droppedDefaultsCount` | The number of default Global Layouts among the Layouts to be migrated that will lose their default designation. |
| `lastRun` | The [outcome](#running) of the most recent run. Absent when the migration has never run. |
| `layoutsCount` | The number of legacy Layouts a run would migrate - the legacy Layouts that haven't been migrated yet. |
| `running` | Whether a migration run is currently in progress on any **io.Manager** Server instance. |
| `status` | The [migration status](#status__reruns) - `"completed-clean"`, `"completed-with-failures"`, or `"never-run"`. |

The `GET /advancedLayouts/migration/layouts` endpoint lists the legacy Layouts a run would migrate. Each item carries the visibility fields of the legacy Layout and an `advancedAccessInfo` field with the access information the Layout will receive, so that you can verify the owner, access level, and access entities of every Layout before migrating it.

## Running

To run the migration, use the ["Migrate legacy layouts"](https://docs.interop.io/manager/capabilities/layouts/admin-ui/index.md#advanced_layouts-migrating_legacy_layouts) page of the Admin UI, or send a request to the `POST /advancedLayouts/migration/run` [endpoint](https://docs.interop.io/manager/capabilities/layouts/overview/index.md#rest_api-layout_migration). The run migrates each legacy Layout that hasn't been migrated yet, and reports its outcome:

| Field | Description |
|-------|-------------|
| `defaultsAssignedCount` | The number of users whose default Global Layout was set by the run. |
| `defaultsSkippedExistingCount` | The number of users that kept their existing default Global Layout. |
| `failures` | The legacy Layouts that the run couldn't migrate, or whose default designation it couldn't carry over, each with the reason for the failure. Contains at most the first 100 failures. |
| `failuresCount` | The total number of failures, including any beyond the reported `failures` list. |
| `finishedAt` | The time when the run finished, in milliseconds since the Unix epoch. |
| `migratedCount` | The number of legacy Layouts migrated by the run. |
| `skippedCount` | The number of legacy Layouts skipped because an earlier run already migrated them. |

> ⚠️ *Note that the run is also recorded in the [audit log](https://docs.interop.io/manager/configuration/server/index.md#configuration_object-auditconfig).*

The `pageSize` property of the [`LayoutsMigrationConfig`](https://docs.interop.io/manager/configuration/server/index.md#configuration_object-layoutsmigrationconfig) object controls how many legacy Layouts a run reads and writes at a time. The Layouts migrated by each page stay migrated if the run fails later.

### Multiple Server Instances

Only one migration run can be in progress at a time across all **io.Manager** Server instances. Starting a run while another one is in progress is rejected with an HTTP 409 status code.

A run is considered in progress for the time set by the `claimStaleness` property of the [`LayoutsMigrationConfig`](https://docs.interop.io/manager/configuration/server/index.md#configuration_object-layoutsmigrationconfig) object (10 minutes by default), measured from its start. After that time, a new run can start even if the earlier one is still executing - e.g., because the server instance running it stopped, or because the run takes longer than `claimStaleness`. Set `claimStaleness` above the expected duration of a run. Before starting a new run after that time has elapsed, confirm that the earlier run has stopped.

## Status & Reruns

After a run, the migration status is one of the following:

| Status | Description |
|--------|-------------|
| `"completed-clean"` | The last run reported no failures and left no legacy Layout unmigrated. The migration is complete and can't be run again - further runs are rejected with an HTTP 409 status code. |
| `"completed-with-failures"` | The last run reported failures, or legacy Layouts that haven't been migrated remain. The migration can be run again. |
| `"never-run"` | No completed run has been recorded. |

A run that fails with an error doesn't change the migration status, but the Layouts it migrated before the error stay migrated and a later run skips them.

### Resolving Failures

When a run reports failures, resolve the reported reasons and run the migration again. A new run skips the legacy Layouts that have already been migrated and migrates only the remaining ones.

- An existing advanced Layout with the same identity is resolved by renaming or deleting that advanced Layout. An existing advanced Layout with the same `id` is resolved by deleting it.
- A default designation that couldn't be carried over because the advanced counterpart is disabled is carried over by the next run once the advanced Layout is enabled again.
- A Layout that couldn't be saved because an advanced Layout with the same identity or `id` was created while the run was in progress is migrated by the next run once the conflict is resolved.
- Any other reason means that the stored legacy Layout record is incomplete or damaged, or that two legacy Layouts would become the same advanced Layout. Correct the record directly in the database, because the [REST API endpoints](https://docs.interop.io/manager/capabilities/layouts/overview/index.md#rest_api-legacy_layouts) for legacy Layouts reject writes while advanced Layouts are enabled.
