Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: handle folders collection #29

Merged
merged 20 commits into from
Jan 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ going to Directus.
Hooks are defined in the configuration file using the `hooks` property. Under this property, you can define the
collection
name and the hook function to be executed.
Available collection names are: `dashboards`, `flows`, `operations`, `panels`, `permissions`, `roles`, `settings`, `translations`,
Available collection names are: `dashboards`, `flows`, `folders`, `operations`, `panels`, `permissions`, `roles`, `settings`, `translations`,
and `webhooks`.

For each collection, available hook functions are: `onQuery`, `onLoad`, `onSave`, and `onDump`.
Expand Down Expand Up @@ -329,6 +329,7 @@ flowchart

- dashboards
- flows
- folders
- operations
- panels
- permissions
Expand Down Expand Up @@ -363,14 +364,18 @@ configurations and schema within Directus. Here is a step-by-step explanation of

Upon execution of the `pull` command, `directus-sync` will:

1. Scan the specified Directus collections, which include dashboards, flows, operations, panels, permissions, roles,
1. Scan the specified Directus collections, which include dashboards, flows, folders, operations, panels, permissions, roles,
settings, translations and webhooks.
2. Assign a SyncID to each element within these collections if it doesn't already have one.
3. Commit the data of these collections into code, allowing for versioning and tracking of configuration changes.

This SyncID tagging facilitates the replication of configurations across different instances of Directus while
maintaining the integrity and links between different entities.

> [!NOTE]
> The original IDs of the flows are preserved to maintain the URLs of the `webhook` type flows.
> The original IDs of the folders are preserved to maintain the associations with fields of the `file` and `image` types.

### Mapping Table

Since it's not possible to add tags directly to entities within Directus, `directus-sync` uses a mapping table that
Expand Down
2 changes: 2 additions & 0 deletions packages/cli/src/lib/loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {
ConfigService,
DashboardsCollection,
FlowsCollection,
FoldersCollection,
OperationsCollection,
PanelsCollection,
PermissionsCollection,
Expand Down Expand Up @@ -63,6 +64,7 @@ export function loadCollections() {
// The collections are populated in the same order
return [
Container.get(SettingsCollection),
Container.get(FoldersCollection),
Container.get(TranslationsCollection),
Container.get(WebhooksCollection),
Container.get(FlowsCollection),
Expand Down
5 changes: 2 additions & 3 deletions packages/cli/src/lib/services/collections/base/data-loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,8 @@ export abstract class DataLoader<DirectusType extends DirectusBaseType> {
*/
async getSourceData(): Promise<WithSyncIdAndWithoutId<DirectusType>[]> {
const { onLoad } = this.hooks;
const loadedData = readJsonSync(
this.filePath,
) as WithSyncIdAndWithoutId<DirectusType>[];
const loadedData: WithSyncIdAndWithoutId<DirectusType>[] =
readJsonSync(this.filePath, { throws: false }) || [];
return onLoad
? await onLoad(loadedData, await this.migrationClient.get())
: loadedData;
Expand Down
45 changes: 45 additions & 0 deletions packages/cli/src/lib/services/collections/folders/collection.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { DirectusCollection } from '../base';
import pino from 'pino';
import { Inject, Service } from 'typedi';
import { FoldersDataLoader } from './data-loader';
import { FoldersDataClient } from './data-client';
import { FoldersIdMapperClient } from './id-mapper-client';
import { FoldersDataDiffer } from './data-differ';
import { getChildLogger } from '../../../helpers';
import { FOLDERS_COLLECTION } from './constants';
import { FoldersDataMapper } from './data-mapper';
import { LOGGER } from '../../../constants';
import { DirectusFolder } from './interfaces';
import { ConfigService } from '../../config';
import { MigrationClient } from '../../migration-client';

@Service()
export class FoldersCollection extends DirectusCollection<DirectusFolder> {
protected readonly enableCreate = true;
protected readonly enableUpdate = true;
protected readonly enableDelete = true;

protected readonly preserveIds = true;

constructor(
@Inject(LOGGER) baseLogger: pino.Logger,
dataDiffer: FoldersDataDiffer,
dataLoader: FoldersDataLoader,
dataClient: FoldersDataClient,
dataMapper: FoldersDataMapper,
idMapper: FoldersIdMapperClient,
config: ConfigService,
migrationClient: MigrationClient,
) {
super(
getChildLogger(baseLogger, FOLDERS_COLLECTION),
dataDiffer,
dataLoader,
dataClient,
dataMapper,
idMapper,
migrationClient,
config.getHooksConfig(FOLDERS_COLLECTION),
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const FOLDERS_COLLECTION = 'folders';
36 changes: 36 additions & 0 deletions packages/cli/src/lib/services/collections/folders/data-client.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { DataClient, Query, WithoutIdAndSyncId } from '../base';
import {
createFolder,
deleteFolder,
readFolders,
updateFolder,
} from '@directus/sdk';
import { Service } from 'typedi';
import { MigrationClient } from '../../migration-client';
import { DirectusFolder } from './interfaces';

@Service()
export class FoldersDataClient extends DataClient<DirectusFolder> {
constructor(migrationClient: MigrationClient) {
super(migrationClient);
}

protected getDeleteCommand(itemId: string) {
return deleteFolder(itemId);
}

protected getInsertCommand(item: WithoutIdAndSyncId<DirectusFolder>) {
return createFolder(item);
}

protected getQueryCommand(query: Query<DirectusFolder>) {
return readFolders(query);
}

protected getUpdateCommand(
itemId: string,
diffItem: Partial<WithoutIdAndSyncId<DirectusFolder>>,
) {
return updateFolder(itemId, diffItem);
}
}
31 changes: 31 additions & 0 deletions packages/cli/src/lib/services/collections/folders/data-differ.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { DataDiffer } from '../base';

import { Inject, Service } from 'typedi';
import { FOLDERS_COLLECTION } from './constants';
import pino from 'pino';
import { FoldersDataLoader } from './data-loader';
import { FoldersDataClient } from './data-client';
import { FoldersIdMapperClient } from './id-mapper-client';
import { getChildLogger } from '../../../helpers';
import { FoldersDataMapper } from './data-mapper';
import { LOGGER } from '../../../constants';
import { DirectusFolder } from './interfaces';

@Service()
export class FoldersDataDiffer extends DataDiffer<DirectusFolder> {
constructor(
@Inject(LOGGER) baseLogger: pino.Logger,
dataLoader: FoldersDataLoader,
dataClient: FoldersDataClient,
dataMapper: FoldersDataMapper,
idMapper: FoldersIdMapperClient,
) {
super(
getChildLogger(baseLogger, FOLDERS_COLLECTION),
dataLoader,
dataClient,
dataMapper,
idMapper,
);
}
}
20 changes: 20 additions & 0 deletions packages/cli/src/lib/services/collections/folders/data-loader.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { DataLoader } from '../base';

import { Service } from 'typedi';
import { FOLDERS_COLLECTION } from './constants';
import path from 'path';
import { DirectusFolder } from './interfaces';
import { ConfigService } from '../../config';
import { MigrationClient } from '../../migration-client';

@Service()
export class FoldersDataLoader extends DataLoader<DirectusFolder> {
constructor(config: ConfigService, migrationClient: MigrationClient) {
const filePath = path.join(
config.getCollectionsConfig().dumpPath,
`${FOLDERS_COLLECTION}.json`,
);
const hooks = config.getHooksConfig(FOLDERS_COLLECTION);
super(filePath, migrationClient, hooks);
}
}
19 changes: 19 additions & 0 deletions packages/cli/src/lib/services/collections/folders/data-mapper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { DataMapper, IdMappers } from '../base';

import { Container, Inject, Service } from 'typedi';
import pino from 'pino';
import { getChildLogger } from '../../../helpers';
import { LOGGER } from '../../../constants';
import { FOLDERS_COLLECTION } from './constants';
import { DirectusFolder } from './interfaces';
import { FoldersIdMapperClient } from './id-mapper-client';

@Service()
export class FoldersDataMapper extends DataMapper<DirectusFolder> {
protected idMappers: IdMappers<DirectusFolder> = {
parent: Container.get(FoldersIdMapperClient),
};
constructor(@Inject(LOGGER) baseLogger: pino.Logger) {
super(getChildLogger(baseLogger, FOLDERS_COLLECTION));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { IdMapperClient } from '../base';
import { Service } from 'typedi';
import { FOLDERS_COLLECTION } from './constants';
import { MigrationClient } from '../../migration-client';

@Service()
export class FoldersIdMapperClient extends IdMapperClient {
constructor(migrationClient: MigrationClient) {
super(migrationClient, FOLDERS_COLLECTION);
}
}
8 changes: 8 additions & 0 deletions packages/cli/src/lib/services/collections/folders/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export * from './interfaces';
export * from './constants';
export * from './collection';
export * from './data-client';
export * from './data-differ';
export * from './data-mapper';
export * from './data-loader';
export * from './id-mapper-client';
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { DirectusFolder as BaseDirectusFolder } from '@directus/sdk';
import { BaseSchema } from '../base';

export type DirectusFolder = BaseDirectusFolder<BaseSchema>;
1 change: 1 addition & 0 deletions packages/cli/src/lib/services/collections/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export * from './base';
export * from './flows';
export * from './folders';
export * from './settings';
export * from './translations';
export * from './webhooks';
Expand Down
1 change: 1 addition & 0 deletions packages/cli/src/lib/services/config/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export const HooksSchema = z.object({
export const OptionsHooksSchema = z.object({
dashboards: HooksSchema.optional(),
flows: HooksSchema.optional(),
folders: HooksSchema.optional(),
operations: HooksSchema.optional(),
panels: HooksSchema.optional(),
permissions: HooksSchema.optional(),
Expand Down
Loading