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

Module versions #1802

Draft
wants to merge 46 commits into
base: development
Choose a base branch
from
Draft
Changes from 1 commit
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
7b1bc03
wip: massive refactor for module versions
niekcandaele Nov 4, 2024
36ccee4
wip: bunch more work
niekcandaele Nov 9, 2024
e97fff5
fix: issue seeding builtin modules
niekcandaele Nov 16, 2024
244c6d1
fix: get hook tests working
niekcandaele Nov 16, 2024
e027c0e
fix: modules not being uninstalled correctly
niekcandaele Nov 16, 2024
247d88f
fix: command triggers
niekcandaele Nov 16, 2024
5dc735e
fix: cronjobs
niekcandaele Nov 16, 2024
5d10740
chore: make snapshot overriding better by being more selective about …
niekcandaele Nov 16, 2024
6a72219
fix: ensure systemConfigSchema gets returned with moduleVersions
niekcandaele Nov 16, 2024
a6b821c
chore: get module tests running
niekcandaele Nov 16, 2024
2d77599
wip perms
niekcandaele Dec 1, 2024
5b97c23
fix: module-related permissions
niekcandaele Dec 7, 2024
a0b6181
fix: handle hooks with dupe names properly
niekcandaele Dec 8, 2024
8a70dce
fix a command test
niekcandaele Dec 8, 2024
6862251
fix: a cronjob test
niekcandaele Dec 8, 2024
c94c7ea
chore: add version IDs as standard ignored for snapshots
niekcandaele Dec 8, 2024
9dbaa63
fix: get all API tests working
niekcandaele Dec 10, 2024
45e29fc
refactor: standardize extends for module related models
niekcandaele Dec 13, 2024
dfc6a6a
fix: get a bunch of module tests running again
niekcandaele Dec 13, 2024
7ea5813
fix: some initial frontend fixes
niekcandaele Dec 14, 2024
61a4a0d
Merge branch development into module-versions
niekcandaele Dec 14, 2024
dc702e8
fix: code style
takaro-ci-bot[bot] Dec 14, 2024
4e71eb1
fix: some more frontend fixes
niekcandaele Dec 15, 2024
8e019bf
fix: allow passing moduleId to tag a new version
niekcandaele Dec 15, 2024
cb9ceb7
generate api client
emielvanseveren Dec 15, 2024
d67fbd3
deps: add semver package
emielvanseveren Dec 15, 2024
ed7415d
fix: code style
takaro-ci-bot[bot] Dec 15, 2024
4a3bdad
feat: add QoL property to list available versions
niekcandaele Dec 15, 2024
1c38430
fix: some snapshots after lib-module fixes
niekcandaele Dec 15, 2024
eef63df
refactor: adjust API endpoints for module creation and updates
niekcandaele Dec 17, 2024
dad18ad
feat: add more protections around module edits
niekcandaele Dec 18, 2024
76d0ec0
feat: export/import of modules now handles ALL versions of a module
niekcandaele Dec 18, 2024
1f390cd
fix: code style
takaro-ci-bot[bot] Dec 18, 2024
dae5036
feat: allow selecting which versions to export
niekcandaele Dec 18, 2024
1f89705
chore: regenerate lib apiclient
emielvanseveren Dec 18, 2024
6455ee1
fix: reinstalling a module now correctly updates config like before
niekcandaele Dec 18, 2024
b089021
wip: module versions frontend
emielvanseveren Dec 19, 2024
ecc5bc3
fix(app-api) make sure update module returns latestVersion
emielvanseveren Dec 19, 2024
9312932
Merge branch 'module-versions' of github.com:gettakaro/takaro into mo…
emielvanseveren Dec 19, 2024
3afed7e
wip: module versions frontend
emielvanseveren Dec 22, 2024
391c413
fix: help command
niekcandaele Dec 22, 2024
802e484
fix: localdev scenario when working on modules
niekcandaele Dec 22, 2024
7d25cd3
chore: refactor module installation ID to be a combination of server …
niekcandaele Dec 22, 2024
132ae36
Chore: tag module from module builder
emielvanseveren Dec 22, 2024
4179321
fix(web-main): do not rely on loader cache only
emielvanseveren Dec 22, 2024
ab42c3c
Merge branch 'module-versions' of github.com:gettakaro/takaro into mo…
emielvanseveren Dec 22, 2024
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
Prev Previous commit
Next Next commit
refactor: adjust API endpoints for module creation and updates
  • Loading branch information
niekcandaele committed Dec 17, 2024
commit eef63df5163573e3306a0e7e40629beda727abc7
6 changes: 5 additions & 1 deletion packages/app-api/src/controllers/Module/modules.ts
Original file line number Diff line number Diff line change
@@ -90,7 +90,7 @@ export class ModuleController {
@UseBefore(AuthService.getAuthMiddleware([PERMISSIONS.MANAGE_MODULES]))
@ResponseSchema(ModuleOutputDTOAPI)
@OpenAPI({
summary: 'Create a new module',
summary: 'Create module',
description: 'Create a new module',
})
@Post('')
@@ -101,6 +101,10 @@ export class ModuleController {

@UseBefore(AuthService.getAuthMiddleware([PERMISSIONS.MANAGE_MODULES]), builtinModuleModificationMiddleware)
@ResponseSchema(ModuleOutputDTOAPI)
@OpenAPI({
summary: 'Update a module',
description: 'Update a module',
})
@Put('/:id')
async update(@Req() req: AuthenticatedRequest, @Params() params: ParamId, @Body() data: ModuleUpdateDTO) {
const service = new ModuleService(req.domainId);
41 changes: 2 additions & 39 deletions packages/app-api/src/controllers/Module/versions.ts
Original file line number Diff line number Diff line change
@@ -3,22 +3,16 @@ import { ITakaroQuery } from '@takaro/db';
import { APIOutput, apiResponse } from '@takaro/http';
import { ModuleService } from '../../service/Module/index.js';
import { AuthenticatedRequest, AuthService } from '../../service/AuthService.js';
import { Body, Get, Post, Delete, JsonController, UseBefore, Req, Put, Params, Res } from 'routing-controllers';
import { Body, Get, Post, JsonController, UseBefore, Req, Params, Res } from 'routing-controllers';
import { OpenAPI, ResponseSchema } from 'routing-controllers-openapi';
import { Type } from 'class-transformer';
import { ParamId } from '../../lib/validators.js';
import { PERMISSIONS } from '@takaro/auth';
import { Response } from 'express';
import { errors } from '@takaro/util';
import { builtinModuleModificationMiddleware } from '../../middlewares/builtinModuleModification.js';
import { BuiltinModule, ICommand, ICommandArgument, ICronJob, IFunction, IHook } from '@takaro/modules';
import { AllowedFilters, RangeFilterCreatedAndUpdatedAt } from '../shared.js';
import {
ModuleExportInputDTO,
ModuleVersionCreateAPIDTO,
ModuleVersionOutputDTO,
ModuleVersionUpdateDTO,
} from '../../service/Module/dto.js';
import { ModuleExportInputDTO, ModuleVersionCreateAPIDTO, ModuleVersionOutputDTO } from '../../service/Module/dto.js';
import { PermissionCreateDTO } from '../../service/RoleService.js';

export class ModuleVersionOutputDTOAPI extends APIOutput<ModuleVersionOutputDTO> {
@@ -105,37 +99,6 @@ export class ModuleVersionController {
return apiResponse(res);
}

@UseBefore(AuthService.getAuthMiddleware([PERMISSIONS.MANAGE_MODULES]), builtinModuleModificationMiddleware)
@ResponseSchema(ModuleVersionOutputDTOAPI)
@OpenAPI({
summary: 'Update a version',
description:
'Update a version of a module, note that you can only update the "latest" version. Tagged versions are immutable',
})
@Put('/:id')
async updateVersion(
@Req() req: AuthenticatedRequest,
@Params() params: ParamId,
@Body() data: ModuleVersionUpdateDTO,
) {
const service = new ModuleService(req.domainId);
const result = await service.updateVersion(params.id, data);
return apiResponse(result);
}

@UseBefore(AuthService.getAuthMiddleware([PERMISSIONS.MANAGE_MODULES]), builtinModuleModificationMiddleware)
@ResponseSchema(APIOutput)
@OpenAPI({
summary: 'Remove a version',
description: 'Removes a version of a module, including all config that is linked to this version',
})
@Delete('/:id')
async removeVersion(@Req() req: AuthenticatedRequest, @Params() params: ParamId) {
const service = new ModuleService(req.domainId);
await service.deleteVersion(params.id);
return apiResponse();
}

@UseBefore(AuthService.getAuthMiddleware([PERMISSIONS.MANAGE_MODULES]))
@ResponseSchema(ModuleVersionOutputDTOAPI)
@OpenAPI({
Original file line number Diff line number Diff line change
@@ -20,32 +20,36 @@ const setup = async function (this: IntegrationTest<ISetupData>): Promise<ISetup

const moduleRes = await this.client.module.moduleControllerCreate({
name: 'Test module',
description: 'Test description',
configSchema: JSON.stringify({
$schema: 'http://json-schema.org/draft-07/schema#',
type: 'object',
properties: {
foo: {
type: 'string',
minLength: 3,
maxLength: 10,
latestVersion: {
description: 'Test description',
configSchema: JSON.stringify({
$schema: 'http://json-schema.org/draft-07/schema#',
type: 'object',
properties: {
foo: {
type: 'string',
minLength: 3,
maxLength: 10,
},
},
},
required: ['foo'],
additionalProperties: false,
}),
required: ['foo'],
additionalProperties: false,
}),
},
});

const cronjobModuleCreateRes = await this.client.module.moduleControllerCreate({
name: 'Test module cronjobs',
description: 'Test description',
configSchema: JSON.stringify({
$schema: 'http://json-schema.org/draft-07/schema#',
type: 'object',
properties: {},
required: [],
additionalProperties: false,
}),
latestVersion: {
description: 'Test description',
configSchema: JSON.stringify({
$schema: 'http://json-schema.org/draft-07/schema#',
type: 'object',
properties: {},
required: [],
additionalProperties: false,
}),
},
});

await this.client.cronjob.cronJobControllerCreate({
Original file line number Diff line number Diff line change
@@ -42,7 +42,9 @@ const tests = [
return (
await this.client.module.moduleControllerCreate({
name: 'Test module',
description: 'bla bla',
latestVersion: {
description: 'bla bla',
},
})
).data.data;
},
@@ -182,7 +184,9 @@ const tests = [
test: async function () {
return this.client.module.moduleControllerCreate({
name: 'Test module',
permissions: [testPermission],
latestVersion: {
permissions: [testPermission],
},
});
},
filteredFields: ['moduleId', 'moduleVersionId'],
@@ -199,9 +203,13 @@ const tests = [
).data.data;
},
test: async function () {
return this.client.module.moduleVersionControllerUpdateVersion(this.setupData.latestVersion.id, {
permissions: [testPermission],
await this.client.module.moduleControllerUpdate(this.setupData.id, {
latestVersion: {
permissions: [testPermission],
},
});

return this.client.module.moduleVersionControllerGetModuleVersion(this.setupData.latestVersion.id);
},
filteredFields: ['moduleId', 'moduleVersionId'],
}),
@@ -213,17 +221,25 @@ const tests = [
return (
await this.client.module.moduleControllerCreate({
name: 'Test module',
permissions: [testPermission],
latestVersion: {
permissions: [testPermission],
},
})
).data.data;
},
test: async function () {
const secondPermission = { permission: 'test2', description: 'test2', friendlyName: 'test2' };
const updateRes = await this.client.module.moduleVersionControllerUpdateVersion(this.setupData.latestVersion.id, {
permissions: [testPermission, secondPermission],
await this.client.module.moduleControllerUpdate(this.setupData.id, {
latestVersion: {
permissions: [testPermission, secondPermission],
},
});

const newPermission = updateRes.data.data.permissions.find((p) => p.permission === 'test');
const versionRes = await this.client.module.moduleVersionControllerGetModuleVersion(
this.setupData.latestVersion.id,
);

const newPermission = versionRes.data.data.permissions.find((p) => p.permission === 'test');
const existingPermission = this.setupData.latestVersion.permissions.find((p) => p.permission === 'test');

if (!existingPermission || !newPermission) {
@@ -232,7 +248,7 @@ const tests = [

expect(existingPermission.id).to.equal(newPermission.id);

return updateRes;
return versionRes;
},
filteredFields: ['moduleId', 'moduleVersionId'],
}),
@@ -244,13 +260,15 @@ const tests = [
return (
await this.client.module.moduleControllerCreate({
name: 'Test module',
permissions: [testPermission],
latestVersion: {
permissions: [testPermission],
},
})
).data.data;
},
test: async function () {
await this.client.module.moduleVersionControllerUpdateVersion(this.setupData.latestVersion.id, {
permissions: [],
await this.client.module.moduleControllerUpdate(this.setupData.id, {
latestVersion: { permissions: [] },
});

const getRes = await this.client.module.moduleControllerGetOne(this.setupData.id);
@@ -269,20 +287,24 @@ const tests = [
return (
await this.client.module.moduleControllerCreate({
name: 'Test module',
permissions: [testPermission],
latestVersion: {
permissions: [testPermission],
},
})
).data.data;
},
test: async function () {
await this.client.module.moduleVersionControllerUpdateVersion(this.setupData.latestVersion.id, {
permissions: [
{
permission: testPermission.permission,
description: 'new description',
friendlyName: 'new friendly name',
canHaveCount: false,
},
],
await this.client.module.moduleControllerUpdate(this.setupData.id, {
latestVersion: {
permissions: [
{
permission: testPermission.permission,
description: 'new description',
friendlyName: 'new friendly name',
canHaveCount: false,
},
],
},
});

const getRes = await this.client.module.moduleControllerGetOne(this.setupData.id);
Original file line number Diff line number Diff line change
@@ -264,7 +264,10 @@ const tests = [
// This is a bug repro, when you delete a user that has events, a FK constraint error is thrown
const role = (await this.client.role.roleControllerSearch({ filters: { name: ['root'] } })).data.data[0];
await this.client.user.userControllerAssignRole(this.setupData.user.id, role.id);
await this.setupData.userClient.module.moduleControllerCreate({ name: 'blabla', description: 'blabla' });
await this.setupData.userClient.module.moduleControllerCreate({
name: 'blabla',
latestVersion: { description: 'blabla' },
});
// So, let's ensure there's an event for this user
const events = await this.client.event.eventControllerSearch({
filters: { actingUserId: [this.setupData.user.id] },
6 changes: 0 additions & 6 deletions packages/app-api/src/db/module.ts
Original file line number Diff line number Diff line change
@@ -268,12 +268,6 @@ export class ModuleRepo extends ITakaroRepo<ModuleModel, ModuleOutputDTO, Module
return !!data;
}

async deleteVersion(id: string): Promise<boolean> {
const { queryVersion } = await this.getModel();
const data = await queryVersion.deleteById(id);
return !!data;
}

async update(id: string, data: ModuleUpdateDTO): Promise<ModuleOutputDTO> {
const { query } = await this.getModel();
const item = await query.updateAndFetchById(id, data.toJSON());
60 changes: 22 additions & 38 deletions packages/app-api/src/service/Module/dto.ts
Original file line number Diff line number Diff line change
@@ -91,10 +91,7 @@ export class ModuleCreateDTO extends TakaroDTO<ModuleCreateDTO> {
builtin: string;
}

export class ModuleCreateAPIDTO extends TakaroDTO<ModuleCreateAPIDTO> {
@IsString()
@Length(3, 50)
name!: string;
export class ModuleCreateVersionInputDTO extends TakaroDTO<ModuleCreateVersionInputDTO> {
@IsString()
@IsOptional()
@Length(1, 5000)
@@ -127,6 +124,16 @@ export class ModuleCreateAPIDTO extends TakaroDTO<ModuleCreateAPIDTO> {
cronJobs: CronJobCreateDTO[];
}

export class ModuleCreateAPIDTO extends TakaroDTO<ModuleCreateAPIDTO> {
@IsString()
@Length(3, 50)
name!: string;
@ValidateNested()
@Type(() => ModuleCreateVersionInputDTO)
@IsOptional()
latestVersion?: ModuleCreateVersionInputDTO;
}

export class ModuleCreateInternalDTO extends TakaroDTO<ModuleCreateInternalDTO> {
@IsString()
@IsOptional()
@@ -135,6 +142,12 @@ export class ModuleCreateInternalDTO extends TakaroDTO<ModuleCreateInternalDTO>
@IsString()
@Length(3, 50)
name!: string;
@ValidateNested()
@Type(() => ModuleCreateVersionInputDTO)
latestVersion: ModuleCreateVersionInputDTO;
}

export class ModuleVersionUpdateDTO extends TakaroDTO<ModuleVersionUpdateDTO> {
@IsString()
@IsOptional()
@Length(1, 5000)
@@ -149,29 +162,17 @@ export class ModuleCreateInternalDTO extends TakaroDTO<ModuleCreateInternalDTO>
@ValidateNested({ each: true })
@Type(() => PermissionCreateDTO)
permissions: PermissionCreateDTO[];
@IsOptional()
@ValidateNested({ each: true })
@Type(() => FunctionCreateDTO)
functions: FunctionCreateDTO[];
@IsOptional()
@ValidateNested({ each: true })
@Type(() => CommandCreateDTO)
commands: CommandCreateDTO[];
@IsOptional()
@ValidateNested({ each: true })
@Type(() => HookCreateDTO)
hooks: HookCreateDTO[];
@IsOptional()
@ValidateNested({ each: true })
@Type(() => CronJobCreateDTO)
cronJobs: CronJobCreateDTO[];
}

export class ModuleUpdateDTO extends TakaroDTO<ModuleUpdateDTO> {
@Length(3, 50)
@IsOptional()
@IsString()
name?: string;

@ValidateNested()
@Type(() => ModuleVersionUpdateDTO)
@IsOptional()
latestVersion?: ModuleVersionUpdateDTO;
}

export class ModuleVersionCreateAPIDTO extends TakaroDTO<ModuleVersionCreateAPIDTO> {
@@ -182,23 +183,6 @@ export class ModuleVersionCreateAPIDTO extends TakaroDTO<ModuleVersionCreateAPID
moduleId: string;
}

export class ModuleVersionUpdateDTO extends TakaroDTO<ModuleVersionUpdateDTO> {
@IsString()
@IsOptional()
@Length(1, 5000)
description?: string;
@IsJSON()
@IsOptional()
configSchema: string;
@IsJSON()
@IsOptional()
uiSchema: string;
@IsOptional()
@ValidateNested({ each: true })
@Type(() => PermissionCreateDTO)
permissions: PermissionCreateDTO[];
}

export class ModuleExportInputDTO extends TakaroDTO<ModuleExportInputDTO> {
@IsUUID()
versionId: string;
Loading