Migrating from v5 to v6
First, execute npx @comet/upgrade@latest v6 in the root of your project.
It automatically installs the new versions of all @comet libraries, runs an ESLint autofix and handles some of the necessary renames.
Renames handled by @comet/upgrade
JobStatus->KubernetesJobStatusin API@SubjectEntity->@AffectedEntityin APIBuildRuntime->JobRuntimein Admin
API
User Permissions
Prerequisites: Manage or sync allowed users in project (not covered here)
Remove custom
CurrentUserandCurrentUserLoader- declare module "@comet/cms-api" {
- interface CurrentUserInterface {
- ...
- }
- }
- @ObjectType()
- export class CurrentUser implements CurrentUserInterface {
- ...
- }
- export class CurrentUserLoader implements CurrentUserLoaderInterface {
- ...
- }Also remove usage
createAuthProxyJwtStrategy({
jwksUri: config.auth.idpJwksUri,
- currentUserLoader: new CurrentUserLoader(),
}),Furthermore, it's not necessary anymore to provide the CurrentUser
createAuthResolver({
- currentUser: CurrentUser,Change imports of removed classes
- import { CurrentUser, CurrentUserLoader } from "@src/auth/current-user";
+ import { CurrentUser } from "@comet/cms-api";Replace occurrences of CurrentUserInterface
- @GetCurrentUser() user: CurrentUserInterface;
+ @GetCurrentUser() user: CurrentUser;It is not possible anymore to use a custom CurrentUserLoader neither to augment/use the CurrentUserInterface.
Create the
AccessControlServicefor theUserPermissionsModule(either in a new module or where it fits best)@Injectable()
export class AccessControlService extends AbstractAccessControlService {
getPermissionsForUser(user: User): PermissionsForUser {
// e.g. return `UserPermissions.allPermissions;` for certain users
}
getContentScopesForUser(user: User): ContentScopesForUser {
// e.g. return `UserPermissions.allContentScopes;` for certain users
}
}Replace
ContentScopeModulewithUserPermissionsModuleRemove
ContentScopeModule:- ContentScopeModule.forRoot({
- ...
- }),Add
UserPermissionsModule:UserPermissionsModule.forRootAsync({
useFactory: (accessControlService: AccessControlService) => ({
availableContentScopes: [/* Array of content Scopes */],
accessControlService,
}),
inject: [AccessControlService],
imports: [/* Modules which provide the services injected in useFactory */],
}),Adapt decorators
Add
@RequiredPermissionto resolvers and controllers+ @RequiredPermission(["pageTree"])
export class PagesResolver {Remove
@AllowForRole(replaced by@RequiredPermission)- @AllowForRole(...)Annotate document entities with
@ScopedEntity()Document entities (e.g.
Page,Link,PredefinedPage) don't have their own scope. Instead, they get their scope from thePageTreeNodethey are attached to. For the scope check to work, you must add the following decorator to the entity:@Entity()
@ObjectType({
implements: () => [DocumentInterface],
})
+ @ScopedEntity(PageTreeNodeDocumentEntityScopeService)
export class Page extends BaseEntity<Page, "id"> implements DocumentInterface {Make sure the scope check can be performed for all operations
All queries and mutations must
- have a
scopeargument, or - be annotated with an
@AffectedEntity()decorator, or - skip the scope check using
@RequiredPermission("examplePermission", { skipScopeCheck: true })
- have a
Optional: Add the
UserService(required for Administration Panel, see Admin)Create a
UserService:// Attention: might already being provided by the library which syncs the users
@Injectable()
export class UserService implements UserPermissionsUserServiceInterface {
getUser(id: string): User {
...
}
findUsers(args: FindUsersArgs): Users {
...
}
}Add it to the
UserPermissionsModule:UserPermissionsModule.forRootAsync({
+ useFactory: (accessControlService: AccessControlService, userService: UserService) => ({
- useFactory: (accessControlService: AccessControlService) => ({
availableContentScopes: [/* Array of content Scopes */],
+ userService,
accessControlService,
}),
+ inject: [AccessControlService, UserService],
- inject: [AccessControlService],
imports: [/* Modules which provide the services injected in useFactory */],
}),
Block Index
Automate the creation of the block index during local development:
- Call
DependenciesService#createViewsin yourFixturesConsole:
// ...
await this.publicUploadsFixtureService.generatePublicUploads();
+ await this.dependenciesService.createViews();
await this.orm.em.flush();
// ...
- Call
createBlockIndexViewsbefore starting the API (after the migrations):
Remove db:migrate from dev-pm.config.js:
{
name: "api",
- script: "npm --prefix api run db:migrate && npm --prefix api run start:dev",
+ script: "npm --prefix api run start:dev",
group: "api",
waitOn: ["tcp:$POSTGRESQL_PORT", "tcp:$IMGPROXY_PORT"],
},
Add db:migrate and createBlockIndexViews to start:dev script in package.json:
- "start:dev": "npm run prebuild && dotenv -c secrets -- nest start --watch --preserveWatchOutput",
+ "start:dev": "npm run prebuild && npm run db:migrate && npm run console createBlockIndexViews && dotenv -c secrets -- nest start --watch --preserveWatchOutput",
Admin
User Permissions
Add
<CurrentUserProvider>beneath<MuiThemeProvider>and move<ErrorDialogHandler>so that it becomes a sibling of<CurrentUserProvider>in App.tsx<MuiThemeProvider theme={theme}>
+ <ErrorDialogHandler />
+ <CurrentUserProvider>Use
useCurrentUser()hook instead requesting the current user from the API- const { loading, data } = useQuery(gql`
- query CurrentUser {
- currentUser {
- ...
- }
- }
- `);
+ const user = useCurrentUser();Also access allowedContentScopes where necessary (e.g. in ContentScopeProvider):
- const allowedUserDomains = data.currentUser.domains;
+ const allowedUserDomains = user.allowedContentScopes.map((contentScope) => contentScope.domain);Optional: Add the Administration Panel
<MenuItemRouterLink
primary={intl.formatMessage({
id: "menu.userPermissions",
defaultMessage: "User Permissions",
})}
icon={<Snips />}
to={`${match.url}/user-permissions`}
/><RouteWithErrorBoundary
path={`${match.path}/user-permissions`}
component={UserPermissionsPage}
/>
Sites Config
The SitesConfigProvider and useSitesConfig were made generic.
You must make following changes in the application:
Define the type of your sites config
Preferably this should be done in
config.ts:export function createConfig() {
// ...
return {
...cometConfig,
apiUrl: environmentVariables.API_URL,
adminUrl: environmentVariables.ADMIN_URL,
+ sitesConfig: JSON.parse(environmentVariables.SITES_CONFIG) as SitesConfig,
};
}
+ export type SitesConfig = Record<string, SiteConfig>;Use the type when using
useSitesConfig- const sitesConfig = useSitesConfig();
+ const sitesConfig = useSitesConfig<SitesConfig>();Optional: Remove type annotation from
ContentScopeProvider#resolveSiteConfigForScope(as it's now inferred)- resolveSiteConfigForScope: (configs: Record<string, SiteConfig>, scope: ContentScope) => configs[scope.domain],
+ resolveSiteConfigForScope: (configs, scope: ContentScope) => configs[scope.domain],
@comet/admin
FinalForm
Previously, FinalForm#onAfterSubmit() automatically executed
stackApi?.goBack();
editDialog?.closeDialog({ delay: true });
This was removed because it was often unwanted and overridden.
You need to:
Add following code if you still want the old behavior:
const stackApi = React.useContext(StackApiContext);
const editDialog = React.useContext(EditDialogApiContext);
// ...
<FinalForm
onAfterSubmit={() => {
stackApi?.goBack();
editDialog?.closeDialog({ delay: true });
}}
>You can remove workarounds like
- onAfterSubmit={() => {
- //don't go back automatically
- }}
@comet/admin-icons
The icons Betrieb, LogischeFilter, Pool, Pool2, Vignette1, Vignette2, StateGreen, StateGreenRing, StateOrange, StateOrangeRing, StateRed and StateRedRing were removed.
If you used any of these icons in your app, you must add them to your project. You can download them here.