Skip to content

Commit 622d17b

Browse files
authored
Version 0.51.0
- add functionality to update metadata via CLI (#1712) - experimental support for `.yaml`, `.svo2`, `.tum`, `.db3` files (#1758) - dependency updates
2 parents c90bc9f + 95ad76d commit 622d17b

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+884
-684
lines changed

.prettierignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@
1515
# ignore bag and mcap files
1616
**/*.mcap
1717
**/*.bag
18+
**/*.db3
19+
**/*.tum
20+
**/*.svo2
21+
**/*.yaml
1822

1923
# ignore build info file
2024
frontend/src/build.ts

backend/package.json

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "kleinkram-backend",
3-
"version": "0.50.2",
3+
"version": "0.51.0",
44
"description": "",
55
"author": "",
66
"private": true,
@@ -31,15 +31,15 @@
3131
"@nestjs/swagger": "^8.0.1",
3232
"@nestjs/typeorm": "^10.0.2",
3333
"@opentelemetry/api": "^1.9.0",
34-
"@opentelemetry/exporter-prometheus": "^0.205.0",
35-
"@opentelemetry/exporter-trace-otlp-http": "^0.205.0",
36-
"@opentelemetry/instrumentation-express": "^0.54.3",
37-
"@opentelemetry/instrumentation-fetch": "^0.205.0",
38-
"@opentelemetry/instrumentation-http": "^0.205.0",
39-
"@opentelemetry/instrumentation-nestjs-core": "^0.52.2",
40-
"@opentelemetry/instrumentation-pg": "^0.58.3",
41-
"@opentelemetry/instrumentation-winston": "^0.50.2",
42-
"@opentelemetry/sdk-node": "^0.205.0",
34+
"@opentelemetry/exporter-prometheus": "^0.206.0",
35+
"@opentelemetry/exporter-trace-otlp-http": "^0.206.0",
36+
"@opentelemetry/instrumentation-express": "^0.55.0",
37+
"@opentelemetry/instrumentation-fetch": "^0.206.0",
38+
"@opentelemetry/instrumentation-http": "^0.206.0",
39+
"@opentelemetry/instrumentation-nestjs-core": "^0.53.0",
40+
"@opentelemetry/instrumentation-pg": "^0.59.0",
41+
"@opentelemetry/instrumentation-winston": "^0.51.0",
42+
"@opentelemetry/sdk-node": "^0.206.0",
4343
"@opentelemetry/sdk-trace-base": "^2.0.0",
4444
"@swc/core": "^1.13.3",
4545
"@swc/jest": "^0.2.39",
@@ -54,7 +54,7 @@
5454
"form-data": "^4.0.4",
5555
"googleapis": "^148.0.0",
5656
"jsonwebtoken": "^9.0.2",
57-
"minio": "8.0.5",
57+
"minio": "8.0.6",
5858
"multer": "^2.0.2",
5959
"passport": "^0.7.0",
6060
"passport-github2": "^0.1.12",
@@ -70,7 +70,7 @@
7070
"vue": "^3.5.21",
7171
"vue-json-pretty": "^2.4.0",
7272
"webpack": "^5.99.9",
73-
"winston": "^3.17.0",
73+
"winston": "^3.18.3",
7474
"winston-loki": "^6.0.7"
7575
},
7676
"devDependencies": {
@@ -80,7 +80,7 @@
8080
"@nestjs/schematics": "^11.0.4",
8181
"@nestjs/testing": "^10.0.0",
8282
"@types/cookie-parser": "^1.4.9",
83-
"@types/express": "^5.0.0",
83+
"@types/express": "^5.0.3",
8484
"@types/jest": "^29.5.2",
8585
"@types/node": "^24.0.7",
8686
"@types/supertest": "^6.0.3",

backend/src/endpoints/project/project.controller.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { CreateProject } from '@common/api/types/create-project.dto';
88
import { ProjectDto } from '@common/api/types/project/base-project.dto';
99
import { DeleteProjectResponseDto } from '@common/api/types/project/delete-project-response.dto';
1010
import { ProjectQueryDto } from '@common/api/types/project/project-query.dto';
11-
import { ProjectWithRequiredTags } from '@common/api/types/project/project-with-required-tags';
11+
import { ProjectWithRequiredTagsDto } from '@common/api/types/project/project-with-required-tags.dto';
1212
import { ProjectsDto } from '@common/api/types/project/projects.dto';
1313
import { ResentProjectsDto } from '@common/api/types/project/recent-projects.dto';
1414
import { RemoveTagTypeDto } from '@common/api/types/remove-tag-type.dto';
@@ -64,11 +64,11 @@ export class ProjectController {
6464
@CanReadProject()
6565
@ApiOkResponse({
6666
description: 'Returns the project',
67-
type: ProjectWithRequiredTags,
67+
type: ProjectWithRequiredTagsDto,
6868
})
6969
async getProjectById(
7070
@ParameterUID('uuid') uuid: string,
71-
): Promise<ProjectWithRequiredTags> {
71+
): Promise<ProjectWithRequiredTagsDto> {
7272
return this.projectService.findOne(uuid);
7373
}
7474

@@ -107,6 +107,9 @@ export class ProjectController {
107107
@Query() query: ProjectQueryDto,
108108
@AddUser() user: AuthHeader,
109109
): Promise<ProjectsDto> {
110+
// Convert string 'true'/'false' to boolean
111+
const exactMatch = query.exactMatch === 'true';
112+
110113
return await this.projectService.findMany(
111114
query.projectUuids ?? [],
112115
query.projectPatterns ?? [],
@@ -116,6 +119,7 @@ export class ProjectController {
116119
query.take,
117120
query.creatorUuid,
118121
user.user.uuid,
122+
exactMatch,
119123
);
120124
}
121125

backend/src/serialization.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import {
2727
import { ProjectDto } from '@common/api/types/project/base-project.dto';
2828
import { ProjectWithMissionCountDto } from '@common/api/types/project/project-with-mission-count.dto';
2929
import { ProjectWithMissionsDto } from '@common/api/types/project/project-with-missions.dto';
30+
import { ProjectWithRequiredTagsDto } from '@common/api/types/project/project-with-required-tags.dto';
3031
import { TagDto, TagTypeDto } from '@common/api/types/tags/tags.dto';
3132
import { TopicDto } from '@common/api/types/topic.dto';
3233
import { GroupMembershipDto, UserDto } from '@common/api/types/user.dto';
@@ -282,6 +283,21 @@ export const projectEntityToDtoWithRequiredTags = (
282283
};
283284
};
284285

286+
export const projectEntityToDtoWithMissionCountAndTags = (
287+
project: Project,
288+
): ProjectWithRequiredTagsDto => {
289+
if (project.creator === undefined) {
290+
throw new Error('Creator can never be undefined');
291+
}
292+
293+
return {
294+
...(projectEntityToDto(project) as ProjectWithRequiredTagsDto),
295+
creator: userEntityToDto(project.creator),
296+
missionCount: project.missionCount ?? 0,
297+
requiredTags: project.requiredTags?.map(tagTypeEntityToDto) ?? [],
298+
};
299+
};
300+
285301
export const projectEntityToDtoWithMissions = (
286302
project: Project,
287303
): ProjectWithMissionsDto => {

backend/src/services/file.service.ts

Lines changed: 34 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import {
1919
Injectable,
2020
NotFoundException,
2121
OnModuleInit,
22+
UnsupportedMediaTypeException,
2223
} from '@nestjs/common';
2324
import { InjectRepository } from '@nestjs/typeorm';
2425
import jwt from 'jsonwebtoken';
@@ -519,9 +520,9 @@ export class FileService implements OnModuleInit {
519520

520521
return await externalMinio.presignedUrl(
521522
'GET',
522-
file.type === FileType.MCAP
523-
? env.MINIO_MCAP_BUCKET_NAME
524-
: env.MINIO_BAG_BUCKET_NAME,
523+
file.type === FileType.BAG
524+
? env.MINIO_BAG_BUCKET_NAME
525+
: env.MINIO_MCAP_BUCKET_NAME,
525526
file.uuid, // we use the uuid as the filename in Minio
526527
expires ? 4 * 60 * 60 : 604_800, // 604800 seconds = 1 week
527528
{
@@ -792,15 +793,40 @@ export class FileService implements OnModuleInit {
792793

793794
logger.debug(`Creating temporary access for file: ${filename}`);
794795

795-
// verify that file has ending .bag or .mcap
796-
if (!filename.endsWith('.bag') && !filename.endsWith('.mcap')) {
796+
const fileExtensionToFileTypeMap: ReadonlyMap<
797+
string,
798+
FileType
799+
> = new Map([
800+
['.bag', FileType.BAG],
801+
['.mcap', FileType.MCAP],
802+
['.yaml', FileType.YAML],
803+
['.svo2', FileType.SVO2],
804+
['.tum', FileType.TUM],
805+
['.db3', FileType.DB3],
806+
]);
807+
808+
const supported_file_endings = [
809+
...fileExtensionToFileTypeMap.keys(),
810+
];
811+
812+
if (
813+
!supported_file_endings.some((ending) =>
814+
filename.endsWith(ending),
815+
)
816+
) {
797817
emptyCredentials.error = 'Invalid file ending';
798818
return emptyCredentials;
799819
}
800820

801-
const fileType: FileType = filename.endsWith('.bag')
802-
? FileType.BAG
803-
: FileType.MCAP;
821+
const matchingFileType = supported_file_endings.find((ending) =>
822+
filename.endsWith(ending),
823+
);
824+
if (matchingFileType === undefined)
825+
throw new UnsupportedMediaTypeException();
826+
const fileType: FileType | undefined =
827+
fileExtensionToFileTypeMap.get(matchingFileType);
828+
if (fileType === undefined)
829+
throw new UnsupportedMediaTypeException();
804830

805831
// check if file already exists
806832
const existingFile = await this.fileRepository.exists({

backend/src/services/mission.service.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,9 @@ export class MissionService {
165165
let query = this.missionRepository
166166
.createQueryBuilder('mission')
167167
.leftJoinAndSelect('mission.project', 'project')
168-
.leftJoinAndSelect('mission.creator', 'creator');
168+
.leftJoinAndSelect('mission.creator', 'creator')
169+
.leftJoinAndSelect('mission.tags', 'tag')
170+
.leftJoinAndSelect('tag.tagType', 'tagType');
169171

170172
query = addAccessConstraintsToMissionQuery(query, userUuid);
171173

backend/src/services/project.service.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import { DefaultRightDto } from '@common/api/types/access-control/default-right.
1818
import { DefaultRights } from '@common/api/types/access-control/default-rights';
1919
import { SortOrder } from '@common/api/types/pagination';
2020
import { ProjectDto } from '@common/api/types/project/base-project.dto';
21-
import { ProjectWithRequiredTags } from '@common/api/types/project/project-with-required-tags';
21+
import { ProjectWithRequiredTagsDto } from '@common/api/types/project/project-with-required-tags.dto';
2222
import { ProjectsDto } from '@common/api/types/project/projects.dto';
2323
import { ResentProjectDto } from '@common/api/types/project/recent-projects.dto';
2424
import AccessGroup from '@common/entities/auth/accessgroup.entity';
@@ -32,7 +32,7 @@ import {
3232
import { ConfigService } from '@nestjs/config';
3333
import { AuthHeader } from '../endpoints/auth/parameter-decorator';
3434
import {
35-
projectEntityToDtoWithMissionCount,
35+
projectEntityToDtoWithMissionCountAndTags,
3636
projectEntityToDtoWithRequiredTags,
3737
} from '../serialization';
3838
import { AccessGroupConfig } from '../types/access-group-config';
@@ -77,10 +77,12 @@ export class ProjectService {
7777
take: number,
7878
creatorUuid: string | undefined,
7979
userUuid: string,
80+
exactMatch = false,
8081
): Promise<ProjectsDto> {
8182
let query = this.projectRepository
8283
.createQueryBuilder('project')
83-
.leftJoinAndSelect('project.creator', 'creator');
84+
.leftJoinAndSelect('project.creator', 'creator')
85+
.leftJoinAndSelect('project.requiredTags', 'requiredTags');
8486

8587
query = addAccessConstraintsToProjectQuery(query, userUuid);
8688

@@ -89,6 +91,7 @@ export class ProjectService {
8991
this.projectRepository,
9092
projectUuids,
9193
projectPatterns,
94+
exactMatch,
9295
);
9396

9497
if (sortBy !== undefined) {
@@ -103,15 +106,15 @@ export class ProjectService {
103106

104107
return {
105108
data: projects.map((element) =>
106-
projectEntityToDtoWithMissionCount(element),
109+
projectEntityToDtoWithMissionCountAndTags(element),
107110
),
108111
count,
109112
skip,
110113
take,
111114
};
112115
}
113116

114-
async findOne(uuid: string): Promise<ProjectWithRequiredTags> {
117+
async findOne(uuid: string): Promise<ProjectWithRequiredTagsDto> {
115118
const missionPromise = this.projectRepository
116119
.createQueryBuilder('project')
117120
.where('project.uuid = :uuid', { uuid })

backend/src/services/tag.service.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -291,7 +291,11 @@ export class TagService {
291291
if (name !== '') {
292292
where.name = ILike(`%${name}%`);
293293
}
294-
if (type !== undefined && type !== DataType.ANY) {
294+
if (
295+
type !== undefined &&
296+
type !== DataType.ANY &&
297+
(type as any) !== ''
298+
) {
295299
where.datatype = type;
296300
}
297301
const [tags, count] = await this.tagTypeRepository.findAndCount({

backend/src/services/utilities.ts

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ export const getFilteredProjectIdSubQuery = (
5454
projectRepository: Repository<Project>,
5555
projectIds: string[],
5656
projectPatterns: string[],
57+
exactMatch: boolean,
5758
): SelectQueryBuilder<{ uuid: string }> => {
5859
const query = projectRepository
5960
.createQueryBuilder('project')
@@ -66,14 +67,15 @@ export const getFilteredProjectIdSubQuery = (
6667
}
6768

6869
if (projectPatterns.length > 0) {
69-
query.orWhere(
70-
'LOWER(project.name) LIKE ANY(ARRAY[:...projectPatterns])',
71-
{
72-
projectPatterns: projectPatterns.map(
73-
(pattern) => `%${pattern.toLowerCase()}%`,
74-
),
75-
},
76-
);
70+
const whereStatement = exactMatch
71+
? 'LOWER(project.name) = ANY(ARRAY[:...projectPatterns])'
72+
: 'LOWER(project.name) LIKE ANY(ARRAY[:...projectPatterns])';
73+
74+
query.orWhere(whereStatement, {
75+
projectPatterns: projectPatterns.map(
76+
(pattern) => `%${pattern.toLowerCase()}%`,
77+
),
78+
});
7779
}
7880

7981
return query;
@@ -220,6 +222,7 @@ export const addProjectFilters = (
220222
projectRepository: Repository<Project>,
221223
projectIds: string[],
222224
projectPatterns: string[],
225+
exactMatch = false,
223226
): SelectQueryBuilder<any> => {
224227
if (projectIds.length > 0 || projectPatterns.length > 0) {
225228
const projectLikePatterns = projectPatterns.map((element) =>
@@ -230,6 +233,7 @@ export const addProjectFilters = (
230233
projectRepository,
231234
projectIds,
232235
projectLikePatterns,
236+
exactMatch,
233237
);
234238

235239
query

backend/src/validation/validation-logic.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ export const MISSION_NAME_REGEX = /^[\w\-_]{3,50}$/;
22

33
export const PROJECT_NAME_REGEX = /^[\w\-_]{3,50}$/;
44

5-
export const FILE_NAME_REGEX = /^[\w\-.()]{3,50}.(bag|mcap)$/;
5+
export const FILE_NAME_REGEX = /^[\w\-.()]{3,50}.(bag|mcap|db3|yaml|svo2|tum)$/;
66

77
export const NON_UUID_REGEX =
88
/^(?![0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$)/;

0 commit comments

Comments
 (0)