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
->KubernetesJobStatus
in API@SubjectEntity
->@AffectedEntity
in APIBuildRuntime
->JobRuntime
in Admin
API
User Permissions
Prerequisites: Manage or sync allowed users in project (not covered here)
Remove custom
CurrentUser
andCurrentUserLoader
- 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
AccessControlService
for 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
ContentScopeModule
withUserPermissionsModule
Remove
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
@RequiredPermission
to 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 thePageTreeNode
they 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
scope
argument, 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#createViews
in yourFixturesConsole
:
// ...
await this.publicUploadsFixtureService.generatePublicUploads();
+ await this.dependenciesService.createViews();
await this.orm.em.flush();
// ...
- Call
createBlockIndexViews
before 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.