Skip to content

Commit

Permalink
docs(v4->v5): reduce migration guide
Browse files Browse the repository at this point in the history
  • Loading branch information
thomasdax98 committed Nov 20, 2023
1 parent 81027ea commit be78b93
Showing 1 changed file with 55 additions and 153 deletions.
208 changes: 55 additions & 153 deletions docs/docs/migration/migration-from-v4-to-v5.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,83 +7,6 @@ sidebar_position: 1

## API

### DAM Scoping

DAM scoping can be enabled optionally. You can still use the DAM without scoping.

To enable DAM scoping, you must

In the API:

- Create a DAM folder entity using `createFolderEntity({ Scope: DamScope });`
- Create a DAM file entity using `createFileEntity({ Scope: DamScope, Folder: DamFolder });`
- Pass the `Scope` DTO and the `File` and `Folder` entities when intializing the `DamModule`

(see Admin section for Admin instructions)

See the [Demo project](https://github.com/vivid-planet/comet/pull/976) for an example on how to enable DAM scoping.

#### FilesService.upload()

The method signature changed. The second argument is now an options object containing a `scope` field.

**Before**

```ts
await filesService.upload(file, folderId);
```

**After**

```ts
await filesService.upload(file, { folderId, scope });
```

### Dependencies

Add the new `DependenciesModule` to your `AppModule`.

#### indexData()

Previously, `BlockIndexData` had one field: `damFileIds`. It could only represent dependencies to DAM files. Now, `BlockIndexData` has a generic `dependencies` field. It can represent dependencies to any entity.

The `indexData()` method of a block must be refactored from

```ts
indexData(): BlockIndexData {
return {
damFileIds: this.damFileId ? [this.damFileId] : [],
};
}
```

to

```ts
indexData(): BlockIndexData {
if (this.damFileId === undefined) {
return {};
}

return {
dependencies: [
{
targetEntityName: File.name,
id: this.damFileId,
},
],
};
}
```

#### DiscoverService

If you use the `DiscoverService` in your application, you now need to import the `DependenciesModule` (instead of the `BlocksModule`).

#### BlockIndexService -> DependenciesService

The `BlockIndexService` was renamed to `DependenciesService`.

### blocks-meta.json

The key (type) of OneOfBlocks is now included in the `blocks-meta.json`.
Expand All @@ -93,69 +16,74 @@ This is only a problem if you still have your own `generate-block-types.ts` in y
- Install `@comet/cli` as a dev dependency
- Replace the scripts in the package.json of your admin:

```json
"generate-block-types": "comet generate-block-types --inputs",
"generate-block-types:watch": "chokidar -s \"**/block-meta.json\" -c \"npm run generate-block-types\""
```diff
- "generate-block-types": "dotenv -c -- ts-node generate-block-types.ts",
+ "generate-block-types": "comet generate-block-types --inputs",
```

- Replace the scripts in the package.json of your site:

```json
"generate-block-types": "comet generate-block-types",
"generate-block-types:watch": "chokidar -s \"**/block-meta.json\" -c \"npm run generate-block-types\"",
```diff
- "generate-block-types": "dotenv -c -- ts-node generate-block-types.ts",
+ "generate-block-types": "comet generate-block-types",
```

### @IsOptional() -> @IsUndefinable() and @IsNull()
### indexData()

`class-validator` offers the `@IsOptional()` decorator. `@IsOptional()` allows `undefined` and `null` which can lead to bugs in some cases.
Previously, `BlockIndexData` had one field: `damFileIds`. It could only represent dependencies to DAM files. Now, `BlockIndexData` has a generic `dependencies` field. It can represent dependencies to any entity.

Thus, COMET now offers the more specific `@IsUndefinable()` and `@IsNull()` decorators. Use these decorators from now on and (if possible) replace existing `@IsOptional()` with `@IsUndefinable()` or `@IsNull()`.
The `indexData()` method of a block must be refactored

### PartialType
```diff
indexData(): BlockIndexData {
- return {
- damFileIds: this.damFileId ? [this.damFileId] : [],
- };
+ if (this.damFileId === undefined) {
+ return {};
+ }
+
+ return {
+ dependencies: [
+ {
+ targetEntityName: File.name,
+ id: this.damFileId,
+ },
+ ],
+ };
}
```

COMET now has an own implementation of `PartialType` using `@IsUndefinable()` (instead of `@IsOptional()`). Uses of `PartialType` from `@nestjs/mapped-types` should be replaced with `PartialType` from `@comet/cms-api`.
### FilesService.upload()

### PageTreeNode
The method signature changed. The second argument is now an options object.

`PageTreeNode` now has an `updatedAt` column.
```diff
- await filesService.upload(file, folderId);
+ await filesService.upload(file, { folderId });
```

The `updatedAt` timestamp is set to the current time when the migration is executed.
If you want the timestamp to reflect the `updatedAt` timestamp of the active attached document, you need to write an additional migration for that.
### @IsOptional() -> @IsUndefinable() and @IsNullable()

### SkipBuildInterceptor
`class-validator` offers the `@IsOptional()` decorator that allows `undefined` and `null`.
COMET now offers the more specific `@IsUndefinable()` and `@IsNullable()` decorators to avoid bugs.
Use these decorators from now on and (if possible) replace existing `@IsOptional()` with `@IsUndefinable()` or `@IsNullable()`.

The `SkipBuildInterceptor` was removed. If you want to skip a build for an operation, use the `@SkipBuild()` decorator instead.
### PartialType

COMET now has an own implementation of `PartialType` using `@IsUndefinable()` (instead of `@IsOptional()`).
Uses of `PartialType` from `@nestjs/mapped-types` should be replaced with `PartialType` from `@comet/cms-api`.

## Admin

### Admin Generator

Add the following command to the `package.json` of your admin app:

```json
"admin-generator": "rimraf 'src/*/generated' && comet-admin-generator generate crud-generator-config.ts",
```diff
+ "admin-generator": "rimraf 'src/*/generated' && comet-admin-generator generate crud-generator-config.ts",
```

### DAM Scoping

DAM scoping can be enabled optionally. You can still use the DAM without scoping.

To enable DAM scoping, you must

- Set `scopeParts` in the `DamConfigProvider` (e.g. `<DamConfigProvider value={{ scopeParts: ["domain"] }}>`)
- Render the content scope indicator in the `DamPage`

```tsx
<DamPage renderContentScopeIndicator={(scope) => <ContentScopeIndicator scope={scope} />} />
```

You can access the current DAM scope in the Admin using the `useDamScope()` hook.

(see API section for API instructions)

See the [Demo project](https://github.com/vivid-planet/comet/pull/976) for an example on how to enable DAM scoping.

### Documents (DocumentInterface)

The `DocumentInterface` now requires a `dependencies()` and a `replaceDependenciesInOutput()` method. However, you don't have to implement them yourself.
Expand All @@ -175,9 +103,10 @@ export const Page: DocumentInterface<Pick<GQLPage, "content" | "seo">, GQLPageIn

### Blocks (BlockInterface)

The `BlockInterface` now has the methods `dependencies()` and `replaceDependenciesInOutput()`.
The `BlockInterface` now has the methods `dependencies()` and `replaceDependenciesInOutput()`.
However, you usually don't have to implement them. Only if your block has dependencies to some entity (e.g. a link to a `PageTreeNode` or uses a `DamFile`).

If your block has a dependency to some entity (e.g. a link to a `PageTreeNode` or uses a `DamFile`), you should implement both methods. `dependencies()` returns the dependency information for the block. It could look like this:
`dependencies()` returns the dependency information for the block. It could look like this:

```tsx
dependencies: (state) => {
Expand All @@ -194,7 +123,7 @@ dependencies: (state) => {
}

return dependencies;
},
};
```

`replaceDependenciesInOutput()` replaces the IDs of your dependencies when copying the block to another scope:
Expand All @@ -209,32 +138,18 @@ replaceDependenciesInOutput: (output, replacements) => {
}

return clonedOutput;
},
};
```

### BlockPreview

The `BlockPreview` component was removed. Instead, use `BlockPreviewContent`:

**Before:**

```tsx
const state = linkBlock.input2State(params.value);
return (
<BlockPreview
title={linkBlock.dynamicDisplayName?.(state) ?? linkBlock.displayName}
content={linkBlock.previewContent(state)}
/>
);
```

**After:**
```diff
- const state = linkBlock.input2State(params.value);

```tsx
return (
<BlockPreviewContent block={linkBlock} input={params.value} />
);
- return <BlockPreview title={linkBlock.dynamicDisplayName?.(state) ?? linkBlock.displayName} content={linkBlock.previewContent(state)} />;
+ return <BlockPreviewContent block={linkBlock} input={params.value} />;
```

### @comet/admin
Expand All @@ -256,28 +171,15 @@ If you had a custom implementation of `getOptionSelected()`, you may need to rep

#### FilterBarMoreFilters

The classes of `FilterBarMoreFilters` have changed.

The "More Filter" text is now [wrapped in a `FilterBarButton`](https://github.com/vivid-planet/comet/commit/d0773a1a#diff-1d3c17943c3e5bfbceba204bf3dd787d051b994c286f2bd9c2cbc462530568fdR27-R31). In the process, the `textWrapper` class was removed. Now you must use the `button` class instead.

#### Dirty Handling

When a user makes changes in a form and then navigates away without saving (e.g. by closing the dialog or leaving the page), a dialog is displayed asking them "Do you want to save your changes?". This is called dirty handling.

Previously, we used the custom implemented `DirtyHandler` to do this. Now, we use `RouterPrompt`, which is based on `react-router`.

Most applications only use dirty handling indirectly since it's built into `FinalForm`, `Stack` and `EditDialog`. If your application doesn't use the `DirtyHandler` directly, you don't need to do anything.

If you use the `DirtyHandler`, you need to migrate to the new system. Take a look at [this PR](https://github.com/vivid-planet/comet/pull/1027) and especially at `FinalForm.tsx`, `Stack.tsx` and `EditDialog.tsx` to get an idea of how to do this.

The `textWrapper` class of `FilterBarMoreFilters` was removed. Use the new `button` class instead.

## Site

There were no breaking changes to `@comet/cms-site`. It should work out of the box.


## Eslint

### no-private-sibling-import

The new rule `no-private-sibling-import` bans imports of private sibling files. A sibling file is, for example, a `Foo.gql.ts` file next to a `Foo.ts` file. `Foo.gql.ts` is considered a private sibling of `Foo.ts` and must not be used (imported) by any other file.
The new rule `no-private-sibling-import` bans imports of private sibling files.
Meaning, a file called `Foo.gql.ts` next to a `Foo.ts` can only be imported by `Foo.ts` and no other file.

0 comments on commit be78b93

Please sign in to comment.