diff --git a/.vscode/settings.json b/.vscode/settings.json
index 76379b46f9f819d868e0945e50caec7a8d9987af..aa283c20c26eef71b361ddb2021b4db7cb129993 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -5,6 +5,7 @@
},
"cSpell.words": [
"Coscine",
+ "Orcid",
"pinia",
"RWTH",
"vite",
diff --git a/src/components/banner/Maintenance.vue b/src/components/banner/Maintenance.vue
index 1cf7cba486ec0d59b923730ad6189f8a78eef66a..175e7430047b2eb1c09a7ae3ef22bf56e124010d 100644
--- a/src/components/banner/Maintenance.vue
+++ b/src/components/banner/Maintenance.vue
@@ -11,9 +11,9 @@
{{ `${messageType}${$t("banner.separator")}` }}
</span>
{{ messageBody }}
- <span v-if="maintenance.url">
+ <span v-if="maintenance.href">
{{ $t("banner.maintenance.linkText") }}
- <a :href="maintenance.url" target="_blank"
+ <a :href="maintenance.href" target="_blank"
>{{ $t("banner.maintenance.moreInformation") }}
</a>
</span>
diff --git a/src/components/elements/LoadingIndicator.vue b/src/components/elements/LoadingIndicator.vue
index 4a14710e6d5f7fbcdbf92d27232e8d955f02f347..038951fb7a154f68052f9c7c795f5e47925ad731 100644
--- a/src/components/elements/LoadingIndicator.vue
+++ b/src/components/elements/LoadingIndicator.vue
@@ -18,19 +18,20 @@ import useUserStore from "@/modules/user/store";
import { loadingCounterEventHandler } from "@/plugins/loadingCounter";
import { defineComponent } from "vue";
+import type { StoreDefinition } from "pinia";
export default defineComponent({
setup() {
const mainStore = useMainStore();
- loadingCounterEventHandler(useAdminStore);
- loadingCounterEventHandler(useErrorStore);
- loadingCounterEventHandler(useLoginStore);
- loadingCounterEventHandler(usePidStore);
- loadingCounterEventHandler(useProjectStore);
- loadingCounterEventHandler(useResourceStore);
- loadingCounterEventHandler(useSearchStore);
- loadingCounterEventHandler(useUserStore);
+ loadingCounterEventHandler(useAdminStore as unknown as StoreDefinition);
+ loadingCounterEventHandler(useErrorStore as unknown as StoreDefinition);
+ loadingCounterEventHandler(useLoginStore as unknown as StoreDefinition);
+ loadingCounterEventHandler(usePidStore as unknown as StoreDefinition);
+ loadingCounterEventHandler(useProjectStore as unknown as StoreDefinition);
+ loadingCounterEventHandler(useResourceStore as unknown as StoreDefinition);
+ loadingCounterEventHandler(useSearchStore as unknown as StoreDefinition);
+ loadingCounterEventHandler(useUserStore as unknown as StoreDefinition);
return { mainStore };
},
diff --git a/src/data/mockup/responses/getMetadata.ts b/src/data/mockup/responses/getMetadata.ts
index e9d45b925640bf05a92e87cd738328926207ec5c..1d4c2dd3346dd802df69511cf74ae75330d50391 100644
--- a/src/data/mockup/responses/getMetadata.ts
+++ b/src/data/mockup/responses/getMetadata.ts
@@ -1,25 +1,29 @@
import type {
- FileDto,
- MetadataDto,
+ FileTreeDto,
+ MetadataTreeDto,
TreeDataType,
} from "@coscine/api-client/dist/types/Coscine.Api";
-export const getMetadataTreeResponse: MetadataDto[] = [
+export const getMetadataTreeResponse: MetadataTreeDto[] = [
{
version: "1693212042",
availableVersions: ["1693212042"],
- definition:
- '@base <https://purl.org/coscine/resources/4103cbea-ffa3-40a5-9e5c-b99cc16f0007/folder_1/folder_2/A.txt/@type=metadata&version=1693212042>.\r\n\r\n@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>.\r\n@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#>.\r\n@prefix xsd: <http://www.w3.org/2001/XMLSchema#>.\r\n@prefix ns2: <http://purl.org/dc/terms/>.\r\n\r\n_:b8477997 ns2:created "2023-08-15"^^xsd:date;\r\n ns2:creator "Petar Hristov";\r\n ns2:title "Title inside Form Generator";\r\n a <https://purl.org/coscine/ap/base/>.\r\n',
- format: "text/turtle",
+ definition: {
+ content:
+ '@base <https://purl.org/coscine/resources/4103cbea-ffa3-40a5-9e5c-b99cc16f0007/folder_1/folder_2/A.txt/@type=metadata&version=1693212042>.\r\n\r\n@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>.\r\n@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#>.\r\n@prefix xsd: <http://www.w3.org/2001/XMLSchema#>.\r\n@prefix ns2: <http://purl.org/dc/terms/>.\r\n\r\n_:b8477997 ns2:created "2023-08-15"^^xsd:date;\r\n ns2:creator "Petar Hristov";\r\n ns2:title "Title inside Form Generator";\r\n a <https://purl.org/coscine/ap/base/>.\r\n',
+ type: "text/turtle",
+ },
path: "folder_1/folder_2/A.txt",
type: "Leaf" as TreeDataType.Leaf,
},
{
version: "1692777419",
availableVersions: ["1692777419"],
- definition:
- '@base <https://purl.org/coscine/resources/4103cbea-ffa3-40a5-9e5c-b99cc16f0007/file_1.txt/@type=metadata&version=1692777419>.\r\n\r\n@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>.\r\n@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#>.\r\n@prefix xsd: <http://www.w3.org/2001/XMLSchema#>.\r\n@prefix ns2: <http://purl.org/dc/terms/>.\r\n\r\n_:b8425359 ns2:created "2023-08-18"^^xsd:date;\r\n ns2:creator "Petar Hristov";\r\n ns2:title "file_1";\r\n a <https://purl.org/coscine/ap/base/>.\r\n',
- format: "text/turtle",
+ definition: {
+ content:
+ '@base <https://purl.org/coscine/resources/4103cbea-ffa3-40a5-9e5c-b99cc16f0007/file_1.txt/@type=metadata&version=1692777419>.\r\n\r\n@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>.\r\n@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#>.\r\n@prefix xsd: <http://www.w3.org/2001/XMLSchema#>.\r\n@prefix ns2: <http://purl.org/dc/terms/>.\r\n\r\n_:b8425359 ns2:created "2023-08-18"^^xsd:date;\r\n ns2:creator "Petar Hristov";\r\n ns2:title "file_1";\r\n a <https://purl.org/coscine/ap/base/>.\r\n',
+ type: "text/turtle",
+ },
path: "file_1.txt",
type: "Leaf" as TreeDataType.Leaf,
},
@@ -32,25 +36,29 @@ export const getMetadataTreeResponse: MetadataDto[] = [
"1692341031",
"1692779210",
],
- definition:
- '@base <https://purl.org/coscine/resources/4103cbea-ffa3-40a5-9e5c-b99cc16f0007/file_0.txt/@type=metadata&version=1692779210>.\r\n\r\n@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>.\r\n@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#>.\r\n@prefix xsd: <http://www.w3.org/2001/XMLSchema#>.\r\n@prefix ns2: <http://purl.org/dc/terms/>.\r\n\r\n_:b8425361 ns2:created "2023-08-18"^^xsd:date;\r\n ns2:creator "Petar Hristov";\r\n ns2:title "Revised";\r\n a <https://purl.org/coscine/ap/base/>.\r\n',
- format: "text/turtle",
+ definition: {
+ content:
+ '@base <https://purl.org/coscine/resources/4103cbea-ffa3-40a5-9e5c-b99cc16f0007/file_0.txt/@type=metadata&version=1692779210>.\r\n\r\n@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>.\r\n@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#>.\r\n@prefix xsd: <http://www.w3.org/2001/XMLSchema#>.\r\n@prefix ns2: <http://purl.org/dc/terms/>.\r\n\r\n_:b8425361 ns2:created "2023-08-18"^^xsd:date;\r\n ns2:creator "Petar Hristov";\r\n ns2:title "Revised";\r\n a <https://purl.org/coscine/ap/base/>.\r\n',
+ type: "text/turtle",
+ },
path: "file_0.txt",
type: "Leaf" as TreeDataType.Leaf,
},
{
version: "1693209938",
availableVersions: ["1693209938"],
- definition:
- '@base <https://purl.org/coscine/resources/4103cbea-ffa3-40a5-9e5c-b99cc16f0007/my_folder/file_of_folder.txt/@type=metadata&version=1693209938>.\r\n\r\n@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>.\r\n@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#>.\r\n@prefix xsd: <http://www.w3.org/2001/XMLSchema#>.\r\n@prefix ns2: <http://purl.org/dc/terms/>.\r\n\r\n_:b8477996 ns2:created "2023-08-15"^^xsd:date;\r\n ns2:creator "Petar Hristov";\r\n ns2:title "From Insomnia";\r\n a <https://purl.org/coscine/ap/base/>.\r\n',
- format: "text/turtle",
+ definition: {
+ content:
+ '@base <https://purl.org/coscine/resources/4103cbea-ffa3-40a5-9e5c-b99cc16f0007/my_folder/file_of_folder.txt/@type=metadata&version=1693209938>.\r\n\r\n@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>.\r\n@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#>.\r\n@prefix xsd: <http://www.w3.org/2001/XMLSchema#>.\r\n@prefix ns2: <http://purl.org/dc/terms/>.\r\n\r\n_:b8477996 ns2:created "2023-08-15"^^xsd:date;\r\n ns2:creator "Petar Hristov";\r\n ns2:title "From Insomnia";\r\n a <https://purl.org/coscine/ap/base/>.\r\n',
+ type: "text/turtle",
+ },
path: "my_folder/file_of_folder.txt",
type: "Leaf" as TreeDataType.Leaf,
},
];
-export const getFileTreeResponse: FileDto[] = [
+export const getFileTreeResponse: FileTreeDto[] = [
{
- parentDirectory: "",
+ directory: "",
name: "folder_1",
size: 0,
creationDate: "2023-09-01T16:57:48.2525218+02:00",
@@ -59,7 +67,7 @@ export const getFileTreeResponse: FileDto[] = [
type: "Tree" as TreeDataType.Tree,
},
{
- parentDirectory: "",
+ directory: "",
name: "my_folder",
size: 0,
creationDate: "2023-09-01T16:57:48.2525245+02:00",
@@ -68,7 +76,7 @@ export const getFileTreeResponse: FileDto[] = [
type: "Tree" as TreeDataType.Tree,
},
{
- parentDirectory: "",
+ directory: "",
name: "file_0.txt",
extension: "txt",
size: 2513352,
diff --git a/src/data/mockup/testLogin.ts b/src/data/mockup/testLogin.ts
index 682101e95a31f2f03642f55858c051f9bca43c04..d0679fa5977605667f2db9bfb0529c49781eeeff 100644
--- a/src/data/mockup/testLogin.ts
+++ b/src/data/mockup/testLogin.ts
@@ -2,9 +2,8 @@ import { type LoginState } from "@/modules/login/types";
import { useLocalStorage } from "@vueuse/core";
export const testLoginState: LoginState = {
- currentTosVersion: "1",
+ currentTosVersion: { version: "1", isCurrent: true },
expiredSession: false,
- tosAccepted: { version: ["1"] },
loginStoredData: useLocalStorage("coscine.login.storedData", ""),
loginUrls: {
orcidUrl: "http://example.org",
diff --git a/src/data/mockup/testProject.ts b/src/data/mockup/testProject.ts
index b71c3394d13bb83502ee6fce0f3d6340a9f642c9..dc69feb3759d2abf4597d53859604215f8992cb5 100644
--- a/src/data/mockup/testProject.ts
+++ b/src/data/mockup/testProject.ts
@@ -1,10 +1,13 @@
-import { userMapper, PublicUserDto2ProjectRoleUserDto } from "@/mapping/user";
import {
type VisitedProjectDto,
type ProjectState,
} from "@/modules/project/types";
import type { RoleDto } from "@coscine/api-client/dist/types/Coscine.Api";
-import { testDiscipline, testOrganization, getTestUser } from "./testUser";
+import {
+ testDiscipline,
+ testOrganizationFromShibboleth,
+ getTestShibbolethUser,
+} from "./testUser";
export const testOwnerRole: RoleDto = {
id: "ownerRole",
@@ -26,13 +29,13 @@ export const testSlug = "testProject";
export const testProject: VisitedProjectDto = {
id: "987654321",
displayName: "Test Project",
- projectName: "Test Project Full",
+ name: "Test Project Full",
description: "Test Project Description",
principleInvestigators: "Test PI",
startDate: "2023-01-01",
endDate: "2033-01-01",
disciplines: [testDiscipline],
- organizations: [testOrganization],
+ organizations: [testOrganizationFromShibboleth],
visibility: { id: "1234", displayName: "Project Members" },
slug: testSlug,
invitations: [],
@@ -45,13 +48,13 @@ export const testProject: VisitedProjectDto = {
],
roles: [
{
- projectId: "987654321",
+ project: { id: "987654321" },
role: testOwnerRole,
- user: userMapper.map(PublicUserDto2ProjectRoleUserDto, getTestUser()),
+ user: getTestShibbolethUser(),
},
], // TODO: Beware the object types!
subProjects: [],
- creator: getTestUser().id,
+ creator: { id: getTestShibbolethUser().id },
};
export const testProjectState: ProjectState = {
@@ -59,7 +62,7 @@ export const testProjectState: ProjectState = {
currentSlug: testSlug,
disciplines: [testDiscipline],
licenses: [{ id: "452545", displayName: "TestLicense" }],
- organizations: [testOrganization],
+ organizations: [testOrganizationFromShibboleth],
roles: [testOwnerRole, testMemberRole, testGuestRole],
topLevelProjects: [testProject],
visibilities: [
diff --git a/src/data/mockup/testResource.ts b/src/data/mockup/testResource.ts
index 9cc50478575fb27873203d20e06e60f8575068c6..ecbf1e2ad6a71b05c975fac24b4fab60a66ba7c9 100644
--- a/src/data/mockup/testResource.ts
+++ b/src/data/mockup/testResource.ts
@@ -8,7 +8,7 @@ import {
baseApplicationProfileFormat,
} from "./metadata/applicationProfile";
import { radarFixedValues } from "./metadata/fixedValues";
-import { testDiscipline, getTestUser } from "./testUser";
+import { testDiscipline, getTestShibbolethUser } from "./testUser";
import type {
ResourceTypeInformationDto,
ResourceTypeStatus,
@@ -50,9 +50,9 @@ export const getTestResource: () => Promise<VisitedResourceObject> =
apUrl
);
const resourceObject: VisitedResourceObject = {
- applicationProfile: apUrl,
+ applicationProfile: { uri: apUrl },
archived: false,
- creator: getTestUser().id,
+ creator: getTestShibbolethUser().id,
dateCreated: null,
description: "TestResource",
disciplines: [testDiscipline],
@@ -64,13 +64,13 @@ export const getTestResource: () => Promise<VisitedResourceObject> =
license: {},
pid: "21.11102/eeb8d803-46a1-49ba-a47c-81cd4f49cd65",
rawApplicationProfile: ap,
- resourceName: "TestResource",
+ name: "TestResource",
storedColumns: null,
type: {
- specificType: testResourceType.specificType,
+ specificType: testResourceType.specificType ?? undefined,
id: testResourceType.id,
options: {
- gitLabOptions: {},
+ gitLab: {},
},
},
usageRights: "",
diff --git a/src/data/mockup/testUser.ts b/src/data/mockup/testUser.ts
index 31d7b09eca2e49e6435eb95576ff82ffb21a01bc..7986a741e135f6ff5fe72d1ab823d9d139b57034 100644
--- a/src/data/mockup/testUser.ts
+++ b/src/data/mockup/testUser.ts
@@ -2,48 +2,112 @@ import { type UserState } from "@/modules/user/types";
import type {
DisciplineDto,
UserDto,
+ UserInstituteDto,
UserOrganizationDto,
} from "@coscine/api-client/dist/types/Coscine.Api/api";
-export const testOrganization: UserOrganizationDto = {
- name: "TestOrg",
- readOnly: true,
- rorUri: "example.com",
+export const testOrganizationFromShibboleth: UserOrganizationDto = {
+ displayName: "Test SSO Organization",
+ readOnly: true, // Shibboleth organizations are always read-only
+ uri: "example.com",
};
-export const testInstitute: UserOrganizationDto = {
- name: "TestInstitute",
- readOnly: true,
- rorUri: "example.com#institute",
+export const testInstituteFromShibboleth: UserInstituteDto = {
+ displayName: "Test SSO Institute",
+ readOnly: true, // Shibboleth institutes are always read-only
+ uri: "example.com#institute",
};
+
+export const testOrganizationFromOrcid: UserOrganizationDto = {
+ displayName: "Test ORCiD Organization",
+ readOnly: true, // ORCiD organizations are never read-only
+ uri: "example.com",
+};
+export const testInstituteFromOrcid: UserInstituteDto = {
+ displayName: "Test ORCiD Institute",
+ readOnly: true, // ORCiD institutes are never read-only
+ uri: "example.com#institute",
+};
+
export const testDiscipline: DisciplineDto = {
id: "1",
displayNameDe: "Test",
displayNameEn: "Test",
- url: "example.com",
+ uri: "example.com",
};
export const testLanguage = { id: "1", displayName: "en" };
-export const getTestUser: () => UserDto = () => {
+/**
+ * Generates a mock Shibboleth user data object for testing purposes.
+ *
+ * @returns {UserDto} A mock Shibboleth user data object.
+ */
+export const getTestShibbolethUser: () => UserDto = () => {
+ return {
+ id: "d302cb44-c934-4b54-a581-9765cab96fca",
+ givenName: "Coscine",
+ familyName: "Example",
+ displayName: "Coscine Example",
+ emails: [
+ { email: "example@university.com", isConfirmed: true, isPrimary: true },
+ ],
+ disciplines: [testDiscipline],
+ language: testLanguage,
+ organizations: [testOrganizationFromShibboleth],
+ institutes: [testInstituteFromShibboleth],
+ };
+};
+
+/**
+ * Generates a mock ORCID user data object for testing purposes.
+ *
+ * @returns {UserDto} A mock ORCID user data object.
+ */
+export const getTestOrcidUser: () => UserDto = () => {
return {
id: "d302cb44-c934-4b54-a581-9765cab96fca",
- lastName: "Coscine",
- firstName: "Example",
+ givenName: "Coscine",
+ familyName: "Example",
displayName: "Coscine Example",
- email: "example@example.com",
+ emails: [
+ { email: "example@orcid.com", isConfirmed: true, isPrimary: true },
+ ],
disciplines: [testDiscipline],
language: testLanguage,
- organizations: [testOrganization],
- institutes: [testInstitute],
+ organizations: [testOrganizationFromOrcid],
+ institutes: [testInstituteFromOrcid],
+ };
+};
+
+export const getTestShibbolethUserState: () => UserState = () => {
+ return {
+ userProfile: {
+ disciplines: [testDiscipline],
+ languages: [testLanguage, { id: "2", displayName: "de" }],
+ organizations: [testOrganizationFromShibboleth],
+ institutes: [testInstituteFromShibboleth],
+ titles: [
+ {
+ id: "1",
+ displayName: "Prof.",
+ },
+ {
+ id: "2",
+ displayName: "Dr.",
+ },
+ ],
+ tokens: null,
+ },
+ user: getTestShibbolethUser(),
};
};
-export const getTestUserState: () => UserState = () => {
+export const getTestOrcidUserState: () => UserState = () => {
return {
userProfile: {
disciplines: [testDiscipline],
languages: [testLanguage, { id: "2", displayName: "de" }],
- organizations: [testOrganization],
- institutes: [testInstitute],
+ organizations: [testOrganizationFromShibboleth],
+ institutes: [testInstituteFromShibboleth],
titles: [
{
id: "1",
@@ -56,6 +120,6 @@ export const getTestUserState: () => UserState = () => {
],
tokens: null,
},
- user: getTestUser(),
+ user: getTestOrcidUser(),
};
};
diff --git a/src/mapping/project.ts b/src/mapping/project.ts
index c3653e3a4263e9d719244d7583d70bbe41f4b1cc..9c229fd2e79252bddd9272715744c5f94ab65be8 100644
--- a/src/mapping/project.ts
+++ b/src/mapping/project.ts
@@ -24,6 +24,7 @@ export const OrganizationDto2OrganizationForProjectManipulationDto =
const configuration = new MapperConfiguration((cfg) => {
cfg.createAutoMap(ProjectDto2ProjectForUpdateDto, {
+ name: (opt) => opt.mapFrom((e) => e.name ?? ""),
disciplines: (opt) =>
opt.mapFromUsing(
(e) => e.disciplines,
@@ -45,7 +46,9 @@ const configuration = new MapperConfiguration((cfg) => {
.createAutoMap(VisibilityDto2VisibilityForProjectManipulationDto, {})
.forSourceMember("displayName", (opt) => opt.ignore());
cfg.createAutoMap(DisciplineDto2DisciplineForProjectManipulationDto, {});
- cfg.createAutoMap(OrganizationDto2OrganizationForProjectManipulationDto, {});
+ cfg.createAutoMap(OrganizationDto2OrganizationForProjectManipulationDto, {
+ uri: (opt) => opt.mapFrom((e) => e.uri ?? ""),
+ });
});
export const projectMapper = configuration.createMapper();
diff --git a/src/mapping/resource.ts b/src/mapping/resource.ts
index bad00b9ffc39b0a88d7d9304b0c40b612f3c330a..a393a43d43fddf165ddc921988fbe76abc638f2b 100644
--- a/src/mapping/resource.ts
+++ b/src/mapping/resource.ts
@@ -70,6 +70,8 @@ export const ResourceTypeInformationDto2ResourceTypeOptionsForCreationDto =
const configuration = new MapperConfiguration((cfg) => {
cfg.createAutoMap(ResourceDto2ResourceForUpdateDto, {
+ name: (opt) => opt.mapFrom((e) => e.name ?? ""),
+ usageNote: (opt) => opt.mapFrom((e) => e.usageRights),
license: (opt) =>
opt.mapFromUsing(
(e) => e.license,
@@ -98,31 +100,31 @@ const configuration = new MapperConfiguration((cfg) => {
gitlabResourceTypeOptions: (opt) =>
opt
.mapFromUsing(
- (e) => e.gitLabOptions,
+ (e) => e.gitLab,
GitLabOptionsDto2GitlabResourceTypeOptionsForUpdateDto
)
- .condition((con) => con.gitLabOptions !== undefined),
+ .condition((con) => con.gitLab !== undefined),
rdsResourceTypeOptions: (opt) =>
opt
.mapFromUsing(
- (e) => e.rdsOptions,
+ (e) => e.rds,
RdsOptionsDto2RdsResourceTypeOptionsForManipulationDto
)
- .condition((con) => con.rdsOptions !== undefined),
+ .condition((con) => con.rds !== undefined),
rdsS3ResourceTypeOptions: (opt) =>
opt
.mapFromUsing(
- (e) => e.rdsS3Options,
+ (e) => e.rdsS3,
RdsS3OptionsDto2RdsS3ResourceTypeOptionsForManipulationDto
)
- .condition((con) => con.rdsS3Options !== undefined),
+ .condition((con) => con.rdsS3 !== undefined),
rdsS3WormResourceTypeOptions: (opt) =>
opt
.mapFromUsing(
- (e) => e.rdsS3WormOptions,
+ (e) => e.rdsS3Worm,
RdsS3WormOptionsDto2RdsS3WormResourceTypeOptionsForManipulationDto
)
- .condition((con) => con.rdsS3WormOptions !== undefined),
+ .condition((con) => con.rdsS3Worm !== undefined),
});
cfg.createMap(ResourceTypeInformationDto2ResourceTypeOptionsForCreationDto, {
linkedResourceTypeOptions: (opt) =>
@@ -176,20 +178,20 @@ const configuration = new MapperConfiguration((cfg) => {
});
cfg.createAutoMap(GitLabOptionsDto2GitlabResourceTypeOptionsForUpdateDto, {});
cfg.createAutoMap(RdsOptionsDto2RdsResourceTypeOptionsForManipulationDto, {
- size: (opt) =>
+ quota: (opt) =>
opt.mapFromUsing((e) => e.size, QuotaDto2QuotaForManipulationDto),
});
cfg.createAutoMap(
RdsS3OptionsDto2RdsS3ResourceTypeOptionsForManipulationDto,
{
- size: (opt) =>
+ quota: (opt) =>
opt.mapFromUsing((e) => e.size, QuotaDto2QuotaForManipulationDto),
}
);
cfg.createAutoMap(
RdsS3WormOptionsDto2RdsS3WormResourceTypeOptionsForManipulationDto,
{
- size: (opt) =>
+ quota: (opt) =>
opt.mapFromUsing((e) => e.size, QuotaDto2QuotaForManipulationDto),
}
);
diff --git a/src/mapping/tree.ts b/src/mapping/tree.ts
index 15084f64009a31a773e370b456f3be6c3f89aa30..dc3439feebbf1f67a8f87870b06794229aa70b9d 100644
--- a/src/mapping/tree.ts
+++ b/src/mapping/tree.ts
@@ -3,7 +3,7 @@ import { v4 as uuidv4 } from "uuid";
import type {
TreeDataType,
- FileDto,
+ FileTreeDto,
} from "@coscine/api-client/dist/types/Coscine.Api";
import type {
FileInformation,
@@ -11,12 +11,12 @@ import type {
} from "@/modules/resource/types";
export const TreeDto2FileInformation = new MappingPair<
- FileDto,
+ FileTreeDto,
FileInformation
>();
export const TreeDto2FolderInformation = new MappingPair<
- FileDto,
+ FileTreeDto,
FolderInformation
>();
@@ -24,7 +24,7 @@ const configuration = new MapperConfiguration((cfg) => {
cfg.createMap(TreeDto2FileInformation, {
id: (opt) => opt.mapFrom((_) => uuidv4()),
name: (opt) => opt.mapFrom((dto) => dto.name ?? ""),
- parentDirectory: (opt) => opt.mapFrom((dto) => dto.parentDirectory ?? ""),
+ parentDirectory: (opt) => opt.mapFrom((dto) => dto.directory ?? ""),
path: (opt) => opt.mapFrom((dto) => dto.path ?? ""),
type: (opt) => opt.mapFrom((_) => "Leaf" as TreeDataType.Leaf),
isFolder: (opt) => opt.mapFrom((_) => false),
@@ -38,7 +38,7 @@ const configuration = new MapperConfiguration((cfg) => {
cfg.createMap(TreeDto2FolderInformation, {
id: (opt) => opt.mapFrom((_) => uuidv4()),
name: (opt) => opt.mapFrom((dto) => dto.name ?? ""),
- parentDirectory: (opt) => opt.mapFrom((dto) => dto.parentDirectory ?? ""),
+ parentDirectory: (opt) => opt.mapFrom((dto) => dto.directory ?? ""),
path: (opt) => opt.mapFrom((dto) => dto.path ?? ""),
type: (opt) => opt.mapFrom((_) => "Tree" as TreeDataType.Tree),
isFolder: (opt) => opt.mapFrom((_) => true),
diff --git a/src/mapping/user.ts b/src/mapping/user.ts
index be4394fba71c19f289ff28dc24c519b1cd25162c..2e6b61123768ae3c243c21b8df6d2d28713acdc9 100644
--- a/src/mapping/user.ts
+++ b/src/mapping/user.ts
@@ -5,18 +5,12 @@ import type {
DisciplineForUserManipulationDto,
LanguageDto,
LanguageForUserManipulationDto,
- ProjectRoleUserDto,
- PublicUserDto,
TitleDto,
TitleForUserManipulationDto,
UserDto,
UserForUpdateDto,
} from "@coscine/api-client/dist/types/Coscine.Api";
-export const PublicUserDto2ProjectRoleUserDto = new MappingPair<
- PublicUserDto,
- ProjectRoleUserDto
->();
export const UserDto2UserForUpdateDto = new MappingPair<
UserDto,
UserForUpdateDto
@@ -35,16 +29,11 @@ export const LanguageDto2LanguageForUserManipulationDto = new MappingPair<
>();
const configuration = new MapperConfiguration((cfg) => {
- cfg.createAutoMap(PublicUserDto2ProjectRoleUserDto, {
- emailAddress: (opt) => opt.mapFrom((u) => u.email),
- userId: (opt) => opt.mapFrom((u) => u.id),
- firstName: (opt) => opt.mapFrom((u) => u.firstName),
- lastName: (opt) => opt.mapFrom((u) => u.lastName),
- });
cfg.createMap(UserDto2UserForUpdateDto, {
- firstName: (opt) => opt.auto(),
- lastName: (opt) => opt.auto(),
- email: (opt) => opt.auto(),
+ givenName: (opt) => opt.mapFrom((dto) => dto.givenName ?? ""),
+ familyName: (opt) => opt.mapFrom((dto) => dto.familyName ?? ""),
+ email: (opt) =>
+ opt.mapFrom((dto) => dto.emails?.find((e) => e.isPrimary)?.email ?? ""),
title: (opt) =>
opt
.condition((dto) => undefined !== dto.title)
@@ -61,11 +50,11 @@ const configuration = new MapperConfiguration((cfg) => {
),
institute: (opt) =>
opt.mapFrom((dto) =>
- dto.institutes?.length ? dto.institutes[0].name : ""
+ dto.institutes?.length ? dto.institutes[0].displayName : ""
),
organization: (opt) =>
opt.mapFrom((dto) =>
- dto.organizations?.length ? dto.organizations[0].name : null
+ dto.organizations?.length ? dto.organizations[0].displayName : null
),
});
cfg.createAutoMap(TitleDto2TitleForUserManipulationDto, {});
diff --git a/src/modules/login/store.ts b/src/modules/login/store.ts
index 6a0a3452520d1482cbbb75f2feba3f33ab2c333a..a739ef61ace5cebfcb6d1aa18c26e61804d58b96 100644
--- a/src/modules/login/store.ts
+++ b/src/modules/login/store.ts
@@ -8,10 +8,11 @@ import type { DFNAAIData, DFNAAIInstitution, LoginState } from "./types";
import useMainStore from "@/store/index";
import useNotificationStore from "@/store/notification";
import { useLocalStorage } from "@vueuse/core";
-import { AccountApi, ToSApi, UserApi } from "@coscine/api-client";
+import { AccountApi, SelfApi, TosApi } from "@coscine/api-client";
import type { AxiosError } from "axios";
import axios from "axios";
import type { LoginUrls } from "@coscine/api-client/dist/types/Coscine.Api.STS";
+import { UserTermsOfServiceAcceptDto } from "@coscine/api-client/dist/types/Coscine.Api";
/*
Store variable name is "this.<id>Store"
@@ -145,7 +146,7 @@ export const useLoginStore = defineStore({
async retrieveCurrentTosVersion() {
const notificationStore = useNotificationStore();
try {
- const apiResponse = await ToSApi.getToS();
+ const apiResponse = await TosApi.getTos();
this.currentTosVersion = apiResponse.data.data;
} catch (error) {
// Handle other Status Codes
@@ -171,7 +172,15 @@ export const useLoginStore = defineStore({
async acceptToS(): Promise<boolean> {
const notificationStore = useNotificationStore();
try {
- await UserApi.acceptCurrentToS();
+ // Retrieve the current TOS version if missing
+ if (!this.currentTosVersion?.version) {
+ await this.retrieveCurrentTosVersion();
+ }
+ // Build the DTO and send it
+ const userTermsOfServiceAcceptDto: UserTermsOfServiceAcceptDto = {
+ version: this.currentTosVersion?.version ?? "",
+ };
+ await SelfApi.acceptCurrentTos(userTermsOfServiceAcceptDto);
return true;
} catch (error) {
// Handle other Status Codes
diff --git a/src/modules/pid/pages/Pid.vue b/src/modules/pid/pages/Pid.vue
index 9c4baa200299cb48f8884c24dce28de6147e2ea8..c8622f41cdb64cc204e716dba7e819a38d8a1f58 100644
--- a/src/modules/pid/pages/Pid.vue
+++ b/src/modules/pid/pages/Pid.vue
@@ -149,7 +149,7 @@ import usePidStore from "../store";
import useNotificationStore from "@/store/notification";
import { useVuelidate, type ValidationArgs } from "@vuelidate/core";
import { email, maxLength, required } from "@vuelidate/validators";
-import type { PidEnquiryDto } from "@coscine/api-client/dist/types/Coscine.Api/api";
+import type { PidRequestDto } from "@coscine/api-client/dist/types/Coscine.Api/api";
export default defineComponent({
setup() {
@@ -168,7 +168,7 @@ export default defineComponent({
email: "",
message: "",
sendConfirmationEmail: true,
- } as PidEnquiryDto,
+ } as PidRequestDto,
isLoading: false,
isPidValid: null as boolean | null,
};
@@ -190,7 +190,7 @@ export default defineComponent({
email: { email, required },
message: { required, maxLength: maxLength(5000) },
sendConfirmationEmail: { required },
- } as ValidationArgs<PidEnquiryDto>,
+ } as ValidationArgs<PidRequestDto>,
};
},
diff --git a/src/modules/pid/store.ts b/src/modules/pid/store.ts
index b88c1e36c7293d5af0197036a4a3826f9cbd7658..7bb0e4e56d464248246cf170a86f5c0c1bda6238 100644
--- a/src/modules/pid/store.ts
+++ b/src/modules/pid/store.ts
@@ -5,7 +5,7 @@ import useNotificationStore from "@/store/notification";
import { PidApi } from "@coscine/api-client";
import { AxiosError } from "axios";
import { StatusCodes } from "http-status-codes";
-import type { PidEnquiryDto } from "@coscine/api-client/dist/types/Coscine.Api/api";
+import type { PidRequestDto } from "@coscine/api-client/dist/types/Coscine.Api/api";
/*
Store variable name is "this.<id>Store"
@@ -64,20 +64,20 @@ export const usePidStore = defineStore({
* Contacts the PID owner using the provided PID prefix, ID, and PID enquiry data.
* @param {string} prefix - The PID prefix value.
* @param {string} id - The ID value.
- * @param {PidEnquiryDto} pidEnquiryDto - The PID enquiry data.
+ * @param {PidRequestDto} pidRequestDto - The PID request data.
* @returns {Promise<boolean | null>} A promise that resolves to a boolean indicating whether the contact operation was successful, or null if there was an error.
*/
async contactPidOwner(
prefix: string,
id: string,
- pidEnquiryDto: PidEnquiryDto
+ pidRequestDto: PidRequestDto
): Promise<boolean | null> {
const notificationStore = useNotificationStore();
try {
- const apiResponse = await PidApi.sendEmailToOwner(
+ const apiResponse = await PidApi.sendRequestToOwner(
prefix,
id,
- pidEnquiryDto
+ pidRequestDto
);
// Note: Beware that only 204 (No Content) is considered a success in this implementation.
return apiResponse.status === StatusCodes.NO_CONTENT ? true : false;
diff --git a/src/modules/project/i18n/de.ts b/src/modules/project/i18n/de.ts
index 2d5b7b50a934e4d6ac85c70fe79e96ccca8f981d..d590cb7aef93759d07d8197a562561542d27696f 100644
--- a/src/modules/project/i18n/de.ts
+++ b/src/modules/project/i18n/de.ts
@@ -45,8 +45,8 @@ export default {
"Sie können eine Einladung an diese E-Mail Adresse senden",
alreadyGotRole: "(bereits Mitglied)",
selectRolePlaceholder: "Rolle auswählen",
- firstName: "Vorname",
- lastName: "Nachname",
+ givenName: "Vorname",
+ familyName: "Nachname",
email: "E-Mail",
actions: "Aktionen",
status: "Status",
diff --git a/src/modules/project/i18n/en.ts b/src/modules/project/i18n/en.ts
index 50d55151bea89652b8d492cdbef09bdf85f25099..4c6e5e77af39db53095cf6dbfe3745dff8c8dad6 100644
--- a/src/modules/project/i18n/en.ts
+++ b/src/modules/project/i18n/en.ts
@@ -42,8 +42,8 @@ export default {
searchEmailInvite: "You may send an invitation to this email address",
alreadyGotRole: "(Already added)",
selectRolePlaceholder: "Select Role",
- firstName: "First Name",
- lastName: "Last Name",
+ givenName: "Given Name",
+ familyName: "Family Name",
email: "Email",
actions: "Actions",
status: "Status",
diff --git a/src/modules/project/pages/ConfigurationMetadata.spec.ts b/src/modules/project/pages/ConfigurationMetadata.spec.ts
index e5f2d615f5cb8fe2fe360f1256a2c014e04d0124..00b39509df89acac3ed74a5a510475cb00ca9c97 100644
--- a/src/modules/project/pages/ConfigurationMetadata.spec.ts
+++ b/src/modules/project/pages/ConfigurationMetadata.spec.ts
@@ -21,7 +21,7 @@ import type Vue from "vue";
/* Import of relevant mockup data */
import { testProjectState } from "@/data/mockup/testProject";
-import { getTestUserState } from "@/data/mockup/testUser";
+import { getTestShibbolethUserState } from "@/data/mockup/testUser";
/* Create a local Vue instance */
const localVue = createLocalVue();
@@ -31,7 +31,7 @@ describe("ConfigurationMetadata.vue", () => {
/* Describe Pre-initialization steps */
/* Description of the test */
- test("should enable buttons and update project name", async () => {
+ test("Should enable buttons and update project name", async () => {
/* Test Pre-initialization steps */
/* Mount the Component */
@@ -40,7 +40,7 @@ describe("ConfigurationMetadata.vue", () => {
createSpy: vitest.fn,
initialState: {
project: testProjectState,
- user: getTestUserState(),
+ user: getTestShibbolethUserState(),
},
}),
i18n,
@@ -48,31 +48,29 @@ describe("ConfigurationMetadata.vue", () => {
});
// Check initial state of buttons
- expect(wrapper.get("#DeleteProjectBtn").attributes()["disabled"]).not.toBe(
- "disabled"
- ); // Delete button - active
- expect(wrapper.get("#SubmitProjectBtn").attributes()["disabled"]).toBe(
- "disabled"
- ); // Submit button - disabled
+ expect(
+ (wrapper.get("#DeleteProjectBtn").element as HTMLButtonElement).disabled
+ ).toBeFalsy(); // Delete button - active
+ expect(
+ (wrapper.get("#SubmitProjectBtn").element as HTMLButtonElement).disabled
+ ).toBeTruthy(); // Submit button - disabled
await wrapper.vm.$nextTick();
// Find element (Project Name)
const element = wrapper.get("#ProjectName");
- expect(element.exists()).toBe(true);
+ expect(element.exists()).toBeTruthy();
// Change value of element
await element.setValue("New Test Project");
- expect(wrapper.vm.$data.projectForUpdate.projectName).toBe(
- "New Test Project"
- );
+ expect(wrapper.vm.$data.projectForUpdate.name).toBe("New Test Project");
// Buttons should be enabled
- expect(wrapper.get("#DeleteProjectBtn").attributes()["disabled"]).not.toBe(
- "disabled"
- ); // Delete button - active
- expect(wrapper.get("#SubmitProjectBtn").attributes()["disabled"]).not.toBe(
- "disabled"
- ); // Submit button - active
+ expect(
+ (wrapper.get("#DeleteProjectBtn").element as HTMLButtonElement).disabled
+ ).toBeFalsy(); // Delete button - active
+ expect(
+ (wrapper.get("#SubmitProjectBtn").element as HTMLButtonElement).disabled
+ ).toBeFalsy(); // Submit button - active
});
});
diff --git a/src/modules/project/pages/ConfigurationMetadata.vue b/src/modules/project/pages/ConfigurationMetadata.vue
index 2a638a1097edb03dc4165b463176b4a6cebdd9e9..f39cae2fbf07b972417625858fd2940a385f24a4 100644
--- a/src/modules/project/pages/ConfigurationMetadata.vue
+++ b/src/modules/project/pages/ConfigurationMetadata.vue
@@ -121,7 +121,7 @@ export default defineComponent({
startDate: "",
endDate: "",
keywords: [] as string[],
- projectName: "",
+ name: "",
principleInvestigators: "",
grantId: "",
slug: "",
diff --git a/src/modules/project/pages/CreateProject.vue b/src/modules/project/pages/CreateProject.vue
index 4b0206e347937cac3d0bd217ca289c84989492dd..848e37ffd351249eb42d826e867099683956374b 100644
--- a/src/modules/project/pages/CreateProject.vue
+++ b/src/modules/project/pages/CreateProject.vue
@@ -103,7 +103,7 @@ export default defineComponent({
startDate: moment(new Date()).format("YYYY-MM-DD"),
endDate: moment(new Date()).format("YYYY-MM-DD"),
keywords: [] as string[],
- projectName: "",
+ name: "",
principleInvestigators: "",
grantId: "",
disciplines: [],
@@ -126,7 +126,7 @@ export default defineComponent({
const memberships = this.userStore.user?.organizations;
if (memberships) {
return memberships.some(
- (membership) => membership.rorUri === "https://ror.org/04xfq0f34"
+ (membership) => membership.uri === "https://ror.org/04xfq0f34"
);
}
return false;
diff --git a/src/modules/project/pages/ListProjects.vue b/src/modules/project/pages/ListProjects.vue
index 3c48233bf46be68ac2eba73f03ff9900ba906b8c..b3710c6d512d7ae20c318ff39b2b3bf654a44c12 100644
--- a/src/modules/project/pages/ListProjects.vue
+++ b/src/modules/project/pages/ListProjects.vue
@@ -23,9 +23,9 @@
</template>
<script lang="ts">
-import type { ProjectDto } from "@coscine/api-client/dist/types/Coscine.Api/api";
import { defineComponent } from "vue";
import type { RawLocation } from "vue-router";
+import type { ProjectDto } from "@coscine/api-client/dist/types/Coscine.Api/api";
// import the store for current module
import useProjectStore from "../store";
diff --git a/src/modules/project/pages/Members.vue b/src/modules/project/pages/Members.vue
index 767e1c0b94c6f30ca4c41b4b5e6c004045cb0e8e..5dfd9252198e7e169e1451bd2e77ae41a2dbde61 100644
--- a/src/modules/project/pages/Members.vue
+++ b/src/modules/project/pages/Members.vue
@@ -72,7 +72,7 @@
description-key="page.members.removeSelectedInvitation"
title-key="page.members.deleteInvitationTitle"
:visible="isDeleteInvitedUserModalVisible"
- :selected-user="selectedInvitation.userMail"
+ :selected-user="selectedInvitation.email"
:selected-project="projectName"
@ok="confirmRevokeInvitation"
@close="closeInvitedUserModal"
@@ -227,18 +227,18 @@ export default defineComponent({
// header text react on language changes.
return [
{
- label: this.$t("page.members.firstName"),
- key: "user.firstName",
+ label: this.$t("page.members.givenName"),
+ key: "givenName",
sortable: true,
},
{
- label: this.$t("page.members.lastName"),
- key: "user.lastName",
+ label: this.$t("page.members.familyName"),
+ key: "familyName",
sortable: true,
},
{
label: this.$t("page.members.email"),
- key: "user.emailAddress",
+ key: "email",
sortable: true,
},
{
@@ -310,16 +310,16 @@ export default defineComponent({
resendInvitation(selectedInvitation: ProjectInvitationDto) {
const invitation: ProjectInvitationForProjectManipulationDto = {
- roleId: selectedInvitation.roleId ?? "",
- email: selectedInvitation.userMail ?? "",
+ roleId: selectedInvitation.role?.id ?? "",
+ email: selectedInvitation.email ?? "",
};
const errorText = this.$t("page.members.invitedUserError", {
- email: selectedInvitation.userMail,
+ email: selectedInvitation.email,
}).toString();
const text = this.$t("page.members.invitedUserText", {
- email: selectedInvitation.userMail,
- role: selectedInvitation.roleId
- ? this.getRoleNameFromId(selectedInvitation.roleId)
+ email: selectedInvitation.email,
+ role: selectedInvitation.role?.id
+ ? this.getRoleNameFromId(selectedInvitation.role.id)
: "",
projectName: this.projectName,
}).toString();
@@ -332,17 +332,15 @@ export default defineComponent({
},
revokeInvitation(selectedInvitation: ProjectInvitationDto) {
- this.selectedInvitation.id = selectedInvitation.id;
- this.selectedInvitation.userMail = selectedInvitation.userMail;
- this.selectedInvitation.projectId = selectedInvitation.projectId;
-
+ // Clone the object to avoid changing the original object
+ this.selectedInvitation = structuredClone(selectedInvitation);
this.isDeleteInvitedUserModalVisible = true;
},
async confirmRevokeInvitation() {
if (this.selectedInvitation.id) {
const text = this.$t("page.members.removedUser", {
- email: this.selectedInvitation.userMail,
+ email: this.selectedInvitation.email,
project: this.projectName,
}).toString();
const worked = await this.projectStore.deleteInvitation(
@@ -356,7 +354,7 @@ export default defineComponent({
});
} else {
const errorMsg = this.$t("page.members.deleteExternalUserError", {
- email: this.selectedInvitation.userMail,
+ email: this.selectedInvitation.email,
}).toString();
this.notificationStore.postNotification({
title: this.$t("page.members.userManagement").toString(),
@@ -381,7 +379,7 @@ export default defineComponent({
async setRole(projectRole: ProjectRoleDto) {
if (this.project && this.project.id && projectRole.id) {
- projectRole.projectId = this.project.id;
+ projectRole.project = { id: this.project.id };
if (projectRole.role) {
projectRole.role.displayName =
@@ -431,7 +429,7 @@ export default defineComponent({
async addUser(projectRole: ProjectRoleDto, callback?: () => void) {
if (this.project && this.project.id) {
- projectRole.projectId = this.project.id;
+ projectRole.project = { id: this.project.id };
const text = this.$t("page.members.addedUser", {
project: this.projectName,
@@ -447,7 +445,7 @@ export default defineComponent({
await this.projectStore.addMembershipToProject(this.project.id, {
roleId: projectRole.role?.id,
- userId: projectRole.user?.userId,
+ userId: projectRole.user?.id,
} as ProjectRoleForProjectCreationDto);
await this.getProjectRoles();
this.notificationStore.postNotification({
@@ -464,20 +462,20 @@ export default defineComponent({
this.candidateForInvitation = {
projectId: this.project?.id ?? "",
roleId: projectRole.role?.id ?? "",
- email: projectRole.user?.emailAddress ?? "",
+ email: projectRole.user?.email ?? "",
expired: false,
invited: false,
};
if (this.invitations) {
for (const invitation of this.invitations) {
if (
- invitation.userMail?.toLowerCase() ===
+ invitation.email?.toLowerCase() ===
projectRole.user?.displayName?.toLowerCase() &&
- invitation.expiration
+ invitation.expirationDate
) {
this.candidateForInvitation.invited = true;
const today = new Date();
- const expirationDate = new Date(invitation.expiration);
+ const expirationDate = new Date(invitation.expirationDate);
if (expirationDate <= today) {
this.candidateForInvitation.expired = true;
}
diff --git a/src/modules/project/pages/ProjectPage.vue b/src/modules/project/pages/ProjectPage.vue
index 9a024eafc5606f50026535391edfe92a344b7e41..82fde69430a370b7809a68ebc6b20327866c8046 100644
--- a/src/modules/project/pages/ProjectPage.vue
+++ b/src/modules/project/pages/ProjectPage.vue
@@ -141,7 +141,7 @@ export default defineComponent({
return this.projectStore.currentResources;
},
isEmailValid(): boolean {
- return this.user?.isEmailConfirmed ?? false;
+ return this.user?.emails?.at(0)?.isConfirmed ?? false;
},
isGuest(): boolean | undefined {
return this.projectStore.currentUserRoleIsGuest;
diff --git a/src/modules/project/pages/Quota.vue b/src/modules/project/pages/Quota.vue
index f705e9bb18c0a8e71d1b906e38aa628cb97c0eb8..838336e7b361686e7e70748811503b9a404fc8df 100644
--- a/src/modules/project/pages/Quota.vue
+++ b/src/modules/project/pages/Quota.vue
@@ -187,7 +187,10 @@
<!-- Resource Name Cell Contents -->
<template #cell(name)="data">
- {{ data.item.resourceDisplayName }}
+ {{
+ resources?.find((r) => r.id === data.item.resource?.id)
+ ?.displayName
+ }}
<b-badge
v-if="isArchived(data.item.resourceId)"
pill
@@ -465,7 +468,7 @@ export default defineComponent({
): Promise<void> {
if (
this.project?.id &&
- resourceQuotaDto.resourceId &&
+ resourceQuotaDto.resource?.id &&
resourceQuotaDto.reserved?.value &&
resourceQuotaDto.reserved?.unit
) {
@@ -478,7 +481,7 @@ export default defineComponent({
const success = await this.projectStore.updateResourceQuota(
this.project.id,
- resourceQuotaDto?.resourceId,
+ resourceQuotaDto?.resource.id,
desiredReservedResourceQuota
);
@@ -491,7 +494,9 @@ export default defineComponent({
"page.quota.resourceQuotaChangedSuccessTitle"
).toString(),
body: this.$t("page.quota.resourceQuotaChangedSuccessBody", {
- ResourceName: resourceQuotaDto.resourceDisplayName,
+ ResourceName: this.resources?.find(
+ (r) => r.id === resourceQuotaDto.resource?.id
+ )?.displayName,
AmountInGB: this.toGiB(resourceQuotaDto.reserved),
}).toString(),
});
@@ -556,8 +561,8 @@ export default defineComponent({
minGiB(resourceId: string): number {
// To get the used quota, use the information in the store.
const resourceQuota = this.resourceTypesQuotas
- ?.find((rtq) => rtq.resourceTypeId === this.selectedResourceTypeId)
- ?.resourceQuotas?.find((rq) => rq.resourceId === resourceId);
+ ?.find((rtq) => rtq.resourceType?.id === this.selectedResourceTypeId)
+ ?.resourceQuotas?.find((rq) => rq.resource?.id === resourceId);
const usedQuotaInGiB = this.toGiB(resourceQuota?.used);
return Math.ceil(usedQuotaInGiB || 1);
@@ -573,8 +578,8 @@ export default defineComponent({
// IMPORTANT: Read the summary above cloneSelectedQuotas.
// If done differently this method breaks and the slider can increase infinitely.
const resourceQuota = this.resourceTypesQuotas
- ?.find((rtq) => rtq.resourceTypeId === this.selectedResourceTypeId)
- ?.resourceQuotas?.find((rq) => rq.resourceId === resourceId);
+ ?.find((rtq) => rtq.resourceType?.id === this.selectedResourceTypeId)
+ ?.resourceQuotas?.find((rq) => rq.resource?.id === resourceId);
// Read the current reserved quota in the store
const reservedQuotaInGiB = this.toGiB(resourceQuota?.reserved);
// Calculate the free quota available for increasing this resource's reserved quota
@@ -596,7 +601,7 @@ export default defineComponent({
cloneSelectedQuotas() {
this.selectedQuotas = _.cloneDeep(
this.resourceTypesQuotas?.find(
- (rtq) => rtq.resourceTypeId === this.selectedResourceTypeId
+ (rtq) => rtq.resourceType?.id === this.selectedResourceTypeId
)
);
},
diff --git a/src/modules/project/pages/components/FormMetadata.vue b/src/modules/project/pages/components/FormMetadata.vue
index d7489c613e36552642d222f50cd6ff848cbbf031..f4738c680147d0818d5e6e3ec4edfad20cc87269 100644
--- a/src/modules/project/pages/components/FormMetadata.vue
+++ b/src/modules/project/pages/components/FormMetadata.vue
@@ -7,12 +7,11 @@
label-for="CopyData"
:label="
$t('form.project.copyMetadataLabel', {
- project: parentProject.projectName,
+ project: parentProject.name,
})
"
:is-loading="isLoading"
type="button"
- class="d-flex align-items-center"
>
<b-button
id="project_copy_button"
@@ -21,8 +20,8 @@
:disabled="disabled"
@click.prevent="copyMetadataFromParent"
>
- {{ $t("buttons.copyMetadata") }}</b-button
- >
+ {{ $t("buttons.copyMetadata") }}
+ </b-button>
</CoscineFormGroup>
<!-- Principal Investigators -->
@@ -177,7 +176,7 @@
:multiple="true"
:loading="isLoadingOrganizations"
:hide-selected="true"
- label="name"
+ label="displayName"
track-by="rorUri"
:show-labels="false"
:placeholder="$t('form.project.projectOrganization')"
diff --git a/src/modules/project/pages/components/FormNaming.vue b/src/modules/project/pages/components/FormNaming.vue
index ee22a542434349c971e4908bd182372440c6dbc6..f4c533510d72d73385524facffd86e3715c52012 100644
--- a/src/modules/project/pages/components/FormNaming.vue
+++ b/src/modules/project/pages/components/FormNaming.vue
@@ -10,14 +10,14 @@
>
<b-form-input
id="ProjectName"
- v-model="v$.projectForManipulation.projectName.$model"
+ v-model="v$.projectForManipulation.name.$model"
:state="
- v$.projectForManipulation.projectName.$dirty
- ? !v$.projectForManipulation.projectName.$error
+ v$.projectForManipulation.name.$dirty
+ ? !v$.projectForManipulation.name.$error
: null
"
:placeholder="$t('form.project.projectName')"
- :maxlength="v$.projectForManipulation.projectName.maxLength.$params.max"
+ :maxlength="v$.projectForManipulation.name.maxLength.$params.max"
required
:disabled="disabled"
@input="translateProjectNameToDisplayName"
@@ -25,8 +25,7 @@
<div class="invalid-tooltip">
{{
$t("form.project.projectNameHelp", {
- maxLength:
- v$.projectForManipulation.projectName.maxLength.$params.max,
+ maxLength: v$.projectForManipulation.name.maxLength.$params.max,
})
}}
</div>
@@ -137,7 +136,7 @@ export default defineComponent({
});
const rules = {
projectForManipulation: {
- projectName: { required, maxLength: maxLength(200) },
+ name: { required, maxLength: maxLength(200) },
displayName: { required, maxLength: maxLength(25) },
description: { required, maxLength: maxLength(5000) },
},
@@ -164,12 +163,9 @@ export default defineComponent({
methods: {
translateProjectNameToDisplayName() {
- if (
- !this.isLockedDisplayName &&
- this.projectForManipulation.projectName
- ) {
+ if (!this.isLockedDisplayName && this.projectForManipulation.name) {
this.projectForManipulation.displayName =
- this.projectForManipulation.projectName.substring(0, 25);
+ this.projectForManipulation.name.substring(0, 25);
this.v$.projectForManipulation.displayName?.$touch();
}
},
diff --git a/src/modules/project/pages/components/MembersList.vue b/src/modules/project/pages/components/MembersList.vue
index d985db4870f98dfffd5f8674f332bd601081a29a..fe0de06a0f81561405e6532acc0b4bad4288ed84 100644
--- a/src/modules/project/pages/components/MembersList.vue
+++ b/src/modules/project/pages/components/MembersList.vue
@@ -21,7 +21,7 @@
v-if="
projectRole.user &&
currentUser &&
- projectRole.user.userId === currentUser.id
+ projectRole.user.id === currentUser.id
"
class="badge badge-pill badge-primary ml-2"
>
@@ -34,17 +34,17 @@
<!-- Member Email -->
<a
v-b-tooltip.hover.bottom="{
- title: projectRole.user ? projectRole.user.emailAddress : '',
+ title: projectRole.user ? projectRole.user.email : '',
boundary: 'viewport',
}"
class="text-primary"
:href="
projectRole.user
- ? `mailto:${projectRole.user.emailAddress}`
+ ? `mailto:${projectRole.user.email}`
: undefined
"
>
- {{ projectRole.user ? projectRole.user.emailAddress : "" }}
+ {{ projectRole.user ? projectRole.user.email : "" }}
</a>
</b-col>
@@ -62,7 +62,7 @@
v-if="
projectRole.user &&
currentUser &&
- projectRole.user.userId === currentUser.id
+ projectRole.user.id === currentUser.id
"
class="float-right"
variant="primary"
@@ -152,15 +152,22 @@ export default defineComponent({
},
computed: {
- currentUser(): UserDto | null | undefined {
- return this.userStore.user;
- },
project(): ProjectDto | null {
return this.projectStore.currentProject;
},
projectRoles(): ProjectRoleDto[] | null {
return this.projectStore.currentProjectRolesSorted;
},
+ currentUser(): UserDto | null | undefined {
+ return this.userStore.user;
+ },
+ currentUserProjectRole(): ProjectRoleDto | undefined {
+ return this.projectRoles?.find(
+ (r) =>
+ r.user?.id === this.currentUser?.id &&
+ r.project?.id === this.project?.id
+ );
+ },
isOwner(): boolean | undefined {
return this.projectStore.currentUserRoleIsOwner;
},
@@ -180,7 +187,10 @@ export default defineComponent({
methods: {
leaveProject() {
if (this.project) {
- this.projectStore.deleteProjectAssociation(this.project, null);
+ this.projectStore.deleteProjectAssociation(
+ this.project,
+ this.currentUserProjectRole
+ );
}
},
},
diff --git a/src/modules/project/pages/components/MembersTable.vue b/src/modules/project/pages/components/MembersTable.vue
index 7873fe0496939f07e89730fa9cc2ad9f6bfab388..3019dfe1b13022abead971e255c87bf8fca49925 100644
--- a/src/modules/project/pages/components/MembersTable.vue
+++ b/src/modules/project/pages/components/MembersTable.vue
@@ -29,6 +29,21 @@
<strong class="ml-2">{{ $t("default.loading") }}</strong>
</div>
+ <!-- Project Members - Given Name Column -->
+ <template #cell(givenName)="row">
+ {{ row.item.user.givenName }}
+ </template>
+
+ <!-- Project Members - Family Name Column -->
+ <template #cell(familyName)="row">
+ {{ row.item.user.familyName }}
+ </template>
+
+ <!-- Project Members - Email Column -->
+ <template #cell(email)="row">
+ {{ row.item.user.email }}
+ </template>
+
<!-- Project Members - Role Column -->
<template #cell(role)="row">
<div v-if="row.item.role" class="noOverflow">
@@ -48,10 +63,15 @@
</div>
</template>
+ <!-- Invited Users - Email Column -->
+ <template #cell(userMail)="row">
+ {{ row.item.email }}
+ </template>
+
<!-- Invited Users - Role Column -->
<template #cell(roleId)="row">
<div class="noOverflow">
- {{ getRoleNameFromId(row.item.roleId) }}
+ {{ getRoleNameFromId(row.item.role.id) }}
</div>
</template>
@@ -80,10 +100,10 @@
<template #cell(expiration)="row">
<div
:class="{
- 'text-danger': checkExpiration(row.item.expiration),
+ 'text-danger': isInvitationExpired(row.item.expirationDate),
}"
>
- {{ getStatus(row.item.expiration) }}
+ {{ getInvitationStatus(row.item.expirationDate) }}
</div>
</template>
@@ -97,16 +117,18 @@
variant="danger"
:btn-text="deleteText"
@click="revokeInvitation(selectedInvitation.item)"
- >{{ deleteText }}</b-button
>
+ {{ deleteText }}
+ </b-button>
<!-- Resend Invitation -->
<b-button
class="rightActionBtn"
size="sm"
- :disabled="!checkExpiration(selectedInvitation.item.expiration)"
+ :disabled="!isInvitationExpired(selectedInvitation.item.expiration)"
@click="resendInvitation(selectedInvitation.item)"
- >{{ $t("buttons.resend") }}
+ >
+ {{ $t("buttons.resend") }}
</b-button>
</div>
</template>
@@ -201,14 +223,9 @@ export default defineComponent({
role(item: ProjectRoleDto) {
this.$emit("setRole", item);
},
- checkExpiration(expiration: string) {
+ isInvitationExpired(expirationDateString: string) {
const today = new Date();
- const expirationDate = new Date(expiration);
- if (expirationDate > today) {
- return false;
- } else {
- return true;
- }
+ return new Date(expirationDateString) <= today;
},
getRoleNameFromId(roleId: string): string | null | undefined {
if (this.roles) {
@@ -221,8 +238,8 @@ export default defineComponent({
return null;
}
},
- getStatus(expiration: string) {
- if (this.checkExpiration(expiration)) {
+ getInvitationStatus(expirationDate: string) {
+ if (this.isInvitationExpired(expirationDate)) {
return this.$t("page.members.expiredStatus");
} else {
return this.$t("page.members.pendingStatus");
diff --git a/src/modules/project/pages/components/UserSearchRow.vue b/src/modules/project/pages/components/UserSearchRow.vue
index 4feeb7c9cf83fbb35b50462792177cc44fe7aabc..9b23945f1ed18ce72899b809bc8a36fce6f499cb 100644
--- a/src/modules/project/pages/components/UserSearchRow.vue
+++ b/src/modules/project/pages/components/UserSearchRow.vue
@@ -146,11 +146,9 @@ import useUserStore from "@/modules/user/store";
import type {
ProjectDto,
ProjectRoleDto,
- ProjectRoleUserDto,
PublicUserDto,
RoleDto,
} from "@coscine/api-client/dist/types/Coscine.Api/api";
-import { PublicUserDto2ProjectRoleUserDto, userMapper } from "@/mapping/user";
export default defineComponent({
props: {
@@ -169,7 +167,7 @@ export default defineComponent({
projectMembers: {
default: undefined,
type: [Array, undefined] as PropType<
- (ProjectRoleUserDto | undefined)[] | undefined
+ (PublicUserDto | undefined)[] | undefined
>,
},
isOwner: {
@@ -185,7 +183,7 @@ export default defineComponent({
data() {
return {
newUserRole: {
- projectId: "",
+ project: { id: "" },
role: { id: "" },
user: {},
} as ProjectRoleDto,
@@ -193,7 +191,7 @@ export default defineComponent({
queriedUsers: [] as PublicUserDto[],
searchString: "",
selectableCheck: (user: PublicUserDto) =>
- !this.projectMembers?.some((pm) => pm?.userId === user.id),
+ !this.projectMembers?.some((u) => u?.id === user.id),
selectedAddingUser: null as PublicUserDto | null,
};
},
@@ -214,10 +212,10 @@ export default defineComponent({
},
emailInvitation(): boolean {
if (
- this.newUserRole?.projectId &&
+ this.newUserRole?.project?.id &&
this.newUserRole?.role?.id &&
- !this.newUserRole?.user?.userId &&
- this.newUserRole?.user?.emailAddress
+ !this.newUserRole?.user?.id &&
+ this.newUserRole?.user?.email
) {
return true;
}
@@ -226,9 +224,9 @@ export default defineComponent({
userInvitation(): boolean {
if (
this.newUserRole &&
- this.newUserRole.projectId &&
+ this.newUserRole.project?.id &&
this.newUserRole.user &&
- this.newUserRole.user.userId &&
+ this.newUserRole.user.id &&
this.newUserRole.role &&
this.newUserRole.role.id
) {
@@ -268,11 +266,9 @@ export default defineComponent({
this.$emit("setFilter", filter);
},
selectUser(selectedUser: PublicUserDto | null) {
- this.newUserRole.user = userMapper.map(
- PublicUserDto2ProjectRoleUserDto,
- selectedUser
- );
- this.newUserRole.projectId = this.project?.id;
+ if (!selectedUser) return;
+ this.newUserRole.user = structuredClone(selectedUser);
+ this.newUserRole.project = { id: this.project?.id };
},
selectRole(roleId: string) {
this.newUserRole.role = {
diff --git a/src/modules/project/pages/components/modals/ImportUserModal.vue b/src/modules/project/pages/components/modals/ImportUserModal.vue
index 84ddd19f7188f54eb5590ea67cc455055588c543..86d9e9959ccd2c63072905e101e6917f5ef6cb32 100644
--- a/src/modules/project/pages/components/modals/ImportUserModal.vue
+++ b/src/modules/project/pages/components/modals/ImportUserModal.vue
@@ -20,7 +20,7 @@
:key="additionalProject.id"
:value="additionalProject.id"
>
- {{ additionalProject.projectName }}
+ {{ additionalProject.name }}
</b-form-select-option>
</b-form-select>
</b-col>
@@ -140,7 +140,7 @@ export default defineComponent({
(projectRole) =>
!this.projectRoles?.some(
(currentProjectRole) =>
- projectRole.user?.userId === currentProjectRole.user?.userId
+ projectRole.user?.id === currentProjectRole.user?.id
)
);
this.additionalMembers.forEach((additionalMember) => {
@@ -157,7 +157,7 @@ export default defineComponent({
this.selectedProject = null;
for (const projectRole of this.additionalMembers) {
if (this.project) {
- projectRole.projectId = this.project.id;
+ projectRole.project = { id: this.project.id };
}
this.$emit("addUser", projectRole);
}
diff --git a/src/modules/project/store.ts b/src/modules/project/store.ts
index 5a8885620c745e24b7f4bc49becde9bd09ce9058..127b1f594acb73e0b394d4db167e0da9eca844db 100644
--- a/src/modules/project/store.ts
+++ b/src/modules/project/store.ts
@@ -15,6 +15,7 @@ import {
VisibilityApi,
LicenseApi,
ProjectQuotaApi,
+ SelfApi,
} from "@coscine/api-client";
import type { Route } from "vue-router";
@@ -28,6 +29,7 @@ import type {
ProjectForUpdateDto,
ProjectInvitationDto,
ProjectInvitationForProjectManipulationDto,
+ ProjectInvitationResolveDto,
ProjectQuotaDto,
ProjectQuotaForUpdateDto,
ProjectRoleDto,
@@ -125,7 +127,7 @@ export const useProjectStore = defineStore({
) {
const parentProjects = [] as ProjectDto[];
let currentParentProjectId =
- this.visitedProjects[this.currentSlug].parentId;
+ this.visitedProjects[this.currentSlug].parent?.id;
while (
currentParentProjectId &&
currentParentProjectId !== "00000000-0000-0000-0000-000000000000"
@@ -138,7 +140,7 @@ export const useProjectStore = defineStore({
break;
}
parentProjects.push(parentProject);
- currentParentProjectId = parentProject.parentId;
+ currentParentProjectId = parentProject.parent?.id;
} else {
break;
}
@@ -179,7 +181,7 @@ export const useProjectStore = defineStore({
if (currentRoles && currentUser) {
const userProjectRole = currentRoles.find(
(projectRole) =>
- projectRole.user && projectRole.user.userId === currentUser.id
+ projectRole.user && projectRole.user.id === currentUser.id
);
return userProjectRole ? userProjectRole.role : null;
} else {
@@ -261,14 +263,22 @@ export const useProjectStore = defineStore({
async retrieveInvitations(project: ProjectDto | null) {
const notificationStore = useNotificationStore();
try {
- if (project && project.id && project.slug) {
+ if (project?.id && project?.slug) {
const apiResponse = await ProjectInvitationApi.getProjectInvitations(
project.id
);
this.visitedProjects[project.slug].invitations =
apiResponse.data.data ?? null;
} else {
- console.error("Selected project is null or its ID is undefined.");
+ if (!project) {
+ console.error("Selected project is null.");
+ }
+ if (project && !project.id) {
+ console.error("Selected project's ID is missing.");
+ }
+ if (project && !project.slug) {
+ console.error("Selected project's slug is missing.");
+ }
}
} catch (error) {
// Handle other Status Codes
@@ -284,7 +294,7 @@ export const useProjectStore = defineStore({
): Promise<ResourceDtoIEnumerablePagedResponse> {
const notificationStore = useNotificationStore();
try {
- if (project && project.id && project.slug) {
+ if (project?.id && project?.slug) {
const apiResponse = await ProjectResourceApi.getResourcesForProject(
project.id,
pageNumber,
@@ -293,7 +303,15 @@ export const useProjectStore = defineStore({
);
return apiResponse.data;
} else {
- console.error("Selected project is null or its ID is undefined.");
+ if (!project) {
+ console.error("Selected project is null.");
+ }
+ if (project && !project.id) {
+ console.error("Selected project's ID is missing.");
+ }
+ if (project && !project.slug) {
+ console.error("Selected project's slug is missing.");
+ }
}
} catch (error) {
// Handle other Status Codes
@@ -352,14 +370,22 @@ export const useProjectStore = defineStore({
): Promise<void> {
const notificationStore = useNotificationStore();
try {
- if (project && project.id && project.slug) {
+ if (project?.id && project?.slug) {
const apiResponse = await ProjectQuotaApi.getProjectQuotas(
project.id
);
this.visitedProjects[project.slug].quotas =
apiResponse.data.data ?? [];
} else {
- console.error("Selected project is null or its ID is undefined.");
+ if (!project) {
+ console.error("Selected project is null.");
+ }
+ if (project && !project.id) {
+ console.error("Selected project's ID is missing.");
+ }
+ if (project && !project.slug) {
+ console.error("Selected project's slug is missing.");
+ }
}
} catch (error) {
// Handle other Status Codes
@@ -438,7 +464,7 @@ export const useProjectStore = defineStore({
// Evaluate general resource type - RDS
if (resource.type?.generalType === CoscineResourceTypes.Rds.General) {
resourceTypeOptionsForManipulationDto.rdsResourceTypeOptions = {
- size: desiredReservedQuota,
+ quota: desiredReservedQuota,
};
}
// Evaluate general resource type - RDS S3
@@ -446,7 +472,7 @@ export const useProjectStore = defineStore({
resource.type?.generalType === CoscineResourceTypes.RdsS3.General
) {
resourceTypeOptionsForManipulationDto.rdsS3ResourceTypeOptions = {
- size: desiredReservedQuota,
+ quota: desiredReservedQuota,
};
}
// Evaluate general resource type - RDS S3 WORM
@@ -454,7 +480,7 @@ export const useProjectStore = defineStore({
resource.type?.generalType === CoscineResourceTypes.RdsS3Worm.General
) {
resourceTypeOptionsForManipulationDto.rdsS3WormResourceTypeOptions = {
- size: desiredReservedQuota,
+ quota: desiredReservedQuota,
};
}
// Assign the correct resource type options
@@ -493,7 +519,7 @@ export const useProjectStore = defineStore({
const notificationStore = useNotificationStore();
try {
const organizationDtos = await wrapListRequest((pageNumber: number) =>
- OrganizationApi.getOrganizations(filter, pageNumber, 50)
+ OrganizationApi.getOrganizations(filter, pageNumber, 150)
);
this.organizations = organizationDtos;
} catch (error) {
@@ -532,7 +558,7 @@ export const useProjectStore = defineStore({
const notificationStore = useNotificationStore();
try {
const disciplineDtos = await wrapListRequest((pageNumber: number) =>
- DisciplineApi.getDisciplines(pageNumber, 50, "displayNameDe asc")
+ DisciplineApi.getDisciplines(pageNumber, 150, "displayNameDe asc")
);
this.disciplines = disciplineDtos ?? null;
} catch (error) {
@@ -554,18 +580,22 @@ export const useProjectStore = defineStore({
async resolveProjectInvitation() {
const notificationStore = useNotificationStore();
+
+ // .../?invitationToken=<token>
+ const invitationTokenQueryParameter = "invitationToken";
+
try {
- // .../?invitationToken=<token>
- const invitationToken = this.router.currentRoute.query.invitationToken;
+ const invitationToken =
+ this.router.currentRoute.query[invitationTokenQueryParameter];
if (invitationToken) {
- //TODO: this is incorrect
- await ProjectInvitationApi.resolveProjectInvitation(
- invitationToken.toString()
- );
+ const projectInvitationResolveDto: ProjectInvitationResolveDto = {
+ token: invitationToken.toString(),
+ };
+ await SelfApi.resolveProjectInvitation(projectInvitationResolveDto);
this.refreshProjectInformation();
removeQueryParameterFromUrl(
this.router.currentRoute,
- "invitationToken"
+ invitationTokenQueryParameter
);
}
} catch (error) {
@@ -573,7 +603,7 @@ export const useProjectStore = defineStore({
notificationStore.postApiErrorNotification(error as AxiosError);
removeQueryParameterFromUrl(
this.router.currentRoute,
- "invitationToken"
+ invitationTokenQueryParameter
);
}
},
@@ -692,12 +722,20 @@ export const useProjectStore = defineStore({
async retrieveProjectRoles(project: ProjectDto | null) {
const notificationStore = useNotificationStore();
try {
- if (project && project.slug && project.id) {
+ if (project?.slug && project?.id) {
const apiResponse = await ProjectMemberApi.getMemberships(project.id);
this.visitedProjects[project.slug].roles =
apiResponse.data.data ?? [];
} else {
- console.error("Selected project is null or its ID is undefined.");
+ if (!project) {
+ console.error("Selected project is null.");
+ }
+ if (project && !project.id) {
+ console.error("Selected project's ID is missing.");
+ }
+ if (project && !project.slug) {
+ console.error("Selected project's slug is missing.");
+ }
}
} catch (error) {
// Handle other Status Codes
@@ -708,14 +746,19 @@ export const useProjectStore = defineStore({
async deleteProjectRole(projectRole: ProjectRoleDto): Promise<boolean> {
const notificationStore = useNotificationStore();
try {
- if (projectRole.id && projectRole.projectId) {
+ if (projectRole.id && projectRole.project?.id) {
await ProjectMemberApi.deleteMembership(
- projectRole.projectId,
+ projectRole.project.id,
projectRole.id
);
return true;
} else {
- console.error("There was a problem with the project role.");
+ if (!projectRole.id) {
+ console.error("ProjectRole's ID is missing.");
+ }
+ if (!projectRole.project?.id) {
+ console.error("ProjectRole's project ID is missing.");
+ }
return false;
}
} catch (error) {
@@ -762,7 +805,7 @@ export const useProjectStore = defineStore({
async retrieveApplicationProfileAnalytics(project: ProjectDto | null) {
const notificationStore = useNotificationStore();
try {
- if (project && project.id) {
+ if (project?.id) {
//await ProjectApi.projectCreateApplicationProfile(project.id);
} else {
console.error("Selected project is null or its ID is undefined.");
@@ -776,16 +819,19 @@ export const useProjectStore = defineStore({
async deleteInvitation(projectInvitation: ProjectInvitationDto) {
const notificationStore = useNotificationStore();
try {
- if (projectInvitation.projectId && projectInvitation.id) {
+ if (projectInvitation.project?.id && projectInvitation.id) {
await ProjectInvitationApi.deleteProjectInvitation(
- projectInvitation.projectId,
+ projectInvitation.project.id,
projectInvitation.id
);
return true;
} else {
- console.error(
- "Selected invitation id or projectId is null or undefined."
- );
+ if (!projectInvitation.project?.id) {
+ console.error("ProjectInvitation's projectId is missing.");
+ }
+ if (!projectInvitation.id) {
+ console.error("ProjectInvitation's ID is missing.");
+ }
}
} catch (error) {
// Handle other Status Codes
@@ -812,25 +858,28 @@ export const useProjectStore = defineStore({
async deleteProjectAssociation(
project: ProjectDto,
- projectRole: ProjectRoleDto | null
+ projectRole: ProjectRoleDto | null | undefined
) {
const notificationStore = useNotificationStore();
try {
- if (
- project.slug &&
- projectRole &&
- projectRole.projectId &&
- projectRole.id
- ) {
+ if (project?.slug && projectRole?.project?.id && projectRole?.id) {
await ProjectMemberApi.deleteMembership(
- projectRole.projectId,
+ projectRole.project.id,
projectRole.id
);
this.visitedProjects[project.slug].roles = null;
this.refreshProjectInformation();
this.router.push({ name: "home" });
} else {
- console.error("Selected project is null or its ID is undefined.");
+ if (!project?.slug) {
+ console.error("Project slug is missing.");
+ }
+ if (!projectRole?.project?.id) {
+ console.error("ProjectRole's project ID is missing.");
+ }
+ if (!projectRole?.id) {
+ console.error("ProjectRole's ID is missing.");
+ }
}
} catch (error) {
// Handle other Status Codes
diff --git a/src/modules/resource/components/create-resource/ApplicationProfile.vue b/src/modules/resource/components/create-resource/ApplicationProfile.vue
index afe80d1af34215a440d0a94291b27ae70cf39f53..271f729f9a72bedf87a32f995a3265b61cfcbd7f 100644
--- a/src/modules/resource/components/create-resource/ApplicationProfile.vue
+++ b/src/modules/resource/components/create-resource/ApplicationProfile.vue
@@ -229,7 +229,7 @@ export default defineComponent({
// An application profile has been selected. Set selection in dropdown.
if (this.resourceForCreation.applicationProfile) {
this.selectedApplicationProfile = this.applicationProfiles.find(
- (ap) => ap.baseUri === this.resourceForCreation.applicationProfile
+ (ap) => ap.uri === this.resourceForCreation.applicationProfile.uri
);
}
},
@@ -268,7 +268,7 @@ export default defineComponent({
for (const applicationProfile of applicationProfiles) {
// Extract AP hierarchy from URIs
- const baseUri = applicationProfile.baseUri || "";
+ const baseUri = applicationProfile.uri || "";
let text = baseUri.replace("https://purl.org/coscine/ap/", "");
if (text.charAt(text.length - 1) === "/") {
text = text.substring(0, text.lastIndexOf("/"));
@@ -310,7 +310,7 @@ export default defineComponent({
this.$set(
this.resourceForCreation,
"applicationProfile",
- this.selectedApplicationProfile?.baseUri
+ this.selectedApplicationProfile?.uri
);
this.v$.resourceForCreation.applicationProfile.$touch();
},
diff --git a/src/modules/resource/components/create-resource/Configuration.vue b/src/modules/resource/components/create-resource/Configuration.vue
index bb451d09d7b70559692eb666970848bde4b8a9bf..ba23ee481795b8c10efacce7863205f0b1f09c8e 100644
--- a/src/modules/resource/components/create-resource/Configuration.vue
+++ b/src/modules/resource/components/create-resource/Configuration.vue
@@ -143,18 +143,18 @@
<!-- Popover -->
<b-popover
v-if="
- ((resourceForCreation.resourceTypeOptions?.rdsResourceTypeOptions?.size
+ ((resourceForCreation.resourceTypeOptions?.rdsResourceTypeOptions?.quota
?.value &&
- resourceForCreation.resourceTypeOptions?.rdsResourceTypeOptions?.size
+ resourceForCreation.resourceTypeOptions?.rdsResourceTypeOptions?.quota
?.value <= 0) ||
(resourceForCreation.resourceTypeOptions?.rdsS3ResourceTypeOptions
- ?.size?.value &&
+ ?.quota?.value &&
resourceForCreation.resourceTypeOptions?.rdsS3ResourceTypeOptions
- ?.size?.value <= 0) ||
+ ?.quota?.value <= 0) ||
(resourceForCreation.resourceTypeOptions?.rdsS3WormResourceTypeOptions
- ?.size?.value &&
+ ?.quota?.value &&
resourceForCreation.resourceTypeOptions
- ?.rdsS3WormResourceTypeOptions?.size?.value <= 0)) &&
+ ?.rdsS3WormResourceTypeOptions?.quota?.value <= 0)) &&
selectedResourceTypeInformation?.isQuotaAvailable
"
target="divButtonNext"
@@ -265,8 +265,8 @@ export default defineComponent({
organizations(): string[] {
if (this.userOrganizations) {
return this.userOrganizations
- .filter((organization) => !(organization.rorUri?.indexOf("#") !== -1)) // If does contain "#" it's a sub level organization, otherwise top level
- .map((org) => (org.name ? org.name : "")) // Extract organization display name, could contain empty strings
+ .filter((organization) => !(organization.uri?.indexOf("#") !== -1)) // If does contain "#" it's a sub level organization, otherwise top level
+ .map((org) => (org.displayName ? org.displayName : "")) // Extract organization display name, could contain empty strings
.filter((n) => n); // Filter out empty strings, if any;
}
return [];
diff --git a/src/modules/resource/components/create-resource/ConfigurationSizeSlider.vue b/src/modules/resource/components/create-resource/ConfigurationSizeSlider.vue
index 2828efead43ee7580fdf3e26332ac02e162f76b2..6e04f7dba7dfe3c55be89cef33c7492afd4a654d 100644
--- a/src/modules/resource/components/create-resource/ConfigurationSizeSlider.vue
+++ b/src/modules/resource/components/create-resource/ConfigurationSizeSlider.vue
@@ -134,7 +134,7 @@ export default defineComponent({
if (this.projectStore.currentResourceTypesQuotas) {
const resourceTypeId = this.resourceForCreation.resourceTypeId;
const quota = this.projectStore.currentResourceTypesQuotas.find(
- (q) => q.resourceTypeId === resourceTypeId
+ (q) => q.resourceType?.id === resourceTypeId
);
if (quota) {
// Calculate slider maximum from Reserved and Used
@@ -171,26 +171,26 @@ export default defineComponent({
// Handle general resource type - RDS
if (
this.resourceForCreation.resourceTypeOptions?.rdsResourceTypeOptions
- ?.size?.value !== undefined
+ ?.quota?.value !== undefined
) {
this.sliderValue =
- this.resourceForCreation.resourceTypeOptions?.rdsResourceTypeOptions?.size?.value;
+ this.resourceForCreation.resourceTypeOptions?.rdsResourceTypeOptions?.quota?.value;
}
// Handle general resource type - RDS S3
else if (
this.resourceForCreation.resourceTypeOptions?.rdsS3ResourceTypeOptions
- ?.size?.value !== undefined
+ ?.quota?.value !== undefined
) {
this.sliderValue =
- this.resourceForCreation.resourceTypeOptions?.rdsS3ResourceTypeOptions?.size?.value;
+ this.resourceForCreation.resourceTypeOptions?.rdsS3ResourceTypeOptions?.quota?.value;
}
// Handle general resource type - RDS S3 WORM
else if (
this.resourceForCreation.resourceTypeOptions
- ?.rdsS3WormResourceTypeOptions?.size?.value !== undefined
+ ?.rdsS3WormResourceTypeOptions?.quota?.value !== undefined
) {
this.sliderValue =
- this.resourceForCreation.resourceTypeOptions?.rdsS3WormResourceTypeOptions?.size?.value;
+ this.resourceForCreation.resourceTypeOptions?.rdsS3WormResourceTypeOptions?.quota?.value;
}
}
},
diff --git a/src/modules/resource/components/create-resource/Overview.vue b/src/modules/resource/components/create-resource/Overview.vue
index ad7c461901dd18cc21236ea441cfd95791b62f5d..585a6a20ad9f9179be40e6ba96f5cfba0f49a222 100644
--- a/src/modules/resource/components/create-resource/Overview.vue
+++ b/src/modules/resource/components/create-resource/Overview.vue
@@ -175,32 +175,32 @@ export default defineComponent({
resourceSizeText(): string {
if (
this.resourceForCreation.resourceTypeOptions?.rdsResourceTypeOptions
- ?.size
+ ?.quota
) {
return this.$t("default.gb", {
number: this.toGiB(
this.resourceForCreation.resourceTypeOptions?.rdsResourceTypeOptions
- ?.size
+ ?.quota
),
}).toString();
} else if (
this.resourceForCreation.resourceTypeOptions?.rdsS3ResourceTypeOptions
- ?.size
+ ?.quota
) {
return this.$t("default.gb", {
number: this.toGiB(
this.resourceForCreation.resourceTypeOptions
- ?.rdsS3ResourceTypeOptions?.size
+ ?.rdsS3ResourceTypeOptions?.quota
),
}).toString();
} else if (
this.resourceForCreation.resourceTypeOptions
- ?.rdsS3WormResourceTypeOptions?.size
+ ?.rdsS3WormResourceTypeOptions?.quota
) {
return this.$t("default.gb", {
number: this.toGiB(
this.resourceForCreation.resourceTypeOptions
- ?.rdsS3WormResourceTypeOptions?.size
+ ?.rdsS3WormResourceTypeOptions?.quota
),
}).toString();
} else {
diff --git a/src/modules/resource/components/create-resource/ResourceMetadata.vue b/src/modules/resource/components/create-resource/ResourceMetadata.vue
index 25cebb69bf657dd6c7174385f7b03ceb14abde04..c9920a098e2c55763aaa59ef8a376c349cb4cee6 100644
--- a/src/modules/resource/components/create-resource/ResourceMetadata.vue
+++ b/src/modules/resource/components/create-resource/ResourceMetadata.vue
@@ -12,14 +12,14 @@
>
<b-form-input
id="ResourceName"
- v-model="v$.resourceForUpdate.resourceName.$model"
+ v-model="v$.resourceForUpdate.name.$model"
:state="
- v$.resourceForUpdate.resourceName.$dirty
- ? !v$.resourceForUpdate.resourceName.$error
+ v$.resourceForUpdate.name.$dirty
+ ? !v$.resourceForUpdate.name.$error
: null
"
:placeholder="$t('form.resource.resourceName')"
- :maxlength="v$.resourceForUpdate.resourceName.maxLength.$params.max"
+ :maxlength="v$.resourceForUpdate.name.maxLength.$params.max"
required
:readonly="readonly"
@input="translateResourceNameToDisplayName"
@@ -27,8 +27,7 @@
<div class="invalid-tooltip">
{{
$t("form.resource.resourceNameHelp", {
- maxLength:
- v$.resourceForUpdate.resourceName.maxLength.$params.max,
+ maxLength: v$.resourceForUpdate.name.maxLength.$params.max,
})
}}
</div>
@@ -329,7 +328,7 @@ export default defineComponent({
});
const rules = {
resourceForUpdate: {
- resourceName: { required, maxLength: maxLength(200) },
+ name: { required, maxLength: maxLength(200) },
displayName: { required, maxLength: maxLength(25) },
description: { required, maxLength: maxLength(5000) },
disciplines: { required },
@@ -532,9 +531,9 @@ export default defineComponent({
* Translate the resource name into display name if not locked and assign the first 25 characters
*/
translateResourceNameToDisplayName() {
- if (!this.isLockedDisplayName && this.resourceForUpdate.resourceName) {
+ if (!this.isLockedDisplayName && this.resourceForUpdate.name) {
this.resourceForUpdate.displayName =
- this.resourceForUpdate.resourceName.substring(0, 25);
+ this.resourceForUpdate.name.substring(0, 25);
this.v$.resourceForUpdate.displayName?.$touch();
}
},
diff --git a/src/modules/resource/components/resource-page/FilesView.spec.ts b/src/modules/resource/components/resource-page/FilesView.spec.ts
index 66a621a117744d64d173583c11c5b0520973c7f2..6f58697721fea20a9d9b2b85c6042a9932aa7d3a 100644
--- a/src/modules/resource/components/resource-page/FilesView.spec.ts
+++ b/src/modules/resource/components/resource-page/FilesView.spec.ts
@@ -20,7 +20,7 @@ import { routes } from "@/router";
import type Vue from "vue";
-import { getTestUserState } from "@/data/mockup/testUser";
+import { getTestShibbolethUserState } from "@/data/mockup/testUser";
import { getTestResourceState } from "@/data/mockup/testResource";
import { testProjectState } from "@/data/mockup/testProject";
import useResourceStore from "../../store";
@@ -53,7 +53,7 @@ describe("FilesView.vue", async () => {
initialState: {
project: testProjectState,
resource: await getTestResourceState(),
- user: getTestUserState(),
+ user: getTestShibbolethUserState(),
},
});
diff --git a/src/modules/resource/components/resource-page/FilesView.vue b/src/modules/resource/components/resource-page/FilesView.vue
index ace291b8ef67e76d3df33bfcf28397071d7041ae..da0c9b795c597d12b3efffb60776936be6c6077e 100644
--- a/src/modules/resource/components/resource-page/FilesView.vue
+++ b/src/modules/resource/components/resource-page/FilesView.vue
@@ -185,8 +185,8 @@ import type {
TreeDataType,
ProjectDto,
ResourceTypeInformationDto,
- FileDto,
- MetadataDto,
+ FileTreeDto,
+ MetadataTreeDto,
} from "@coscine/api-client/dist/types/Coscine.Api";
import type {
BilingualLabels,
@@ -776,13 +776,13 @@ export default defineComponent({
/**
* Constructs the folder content based on the provided file and metadata trees.
* @async
- * @param {FileDto[] | null | undefined} fileTree - File tree of the folder.
- * @param {MetadataDto[] | null | undefined} metadataTree - Metadata tree of the folder.
+ * @param {FileTreeDto[] | null | undefined} fileTree - File tree of the folder.
+ * @param {MetadataTreeDto[] | null | undefined} metadataTree - Metadata tree of the folder.
* @returns {FolderContent[]} An array containing folder content.
*/
async constructFolderContents(
- fileTree: FileDto[] | null | undefined,
- metadataTree: MetadataDto[] | null | undefined
+ fileTree: FileTreeDto[] | null | undefined,
+ metadataTree: MetadataTreeDto[] | null | undefined
): Promise<FolderContent[]> {
const folderContents = [];
@@ -798,10 +798,13 @@ export default defineComponent({
const metadataEntry = metadataTree?.find(
(md) => md.path === fileEntry.path
);
- if (metadataEntry?.definition && metadataEntry?.format) {
+ if (
+ metadataEntry?.definition?.type &&
+ metadataEntry?.definition?.content
+ ) {
content.metadata = await parseRDFDefinition(
- metadataEntry.definition,
- metadataEntry.format
+ metadataEntry.definition.content,
+ metadataEntry.definition.type
);
}
folderContents.push(content);
@@ -832,10 +835,10 @@ export default defineComponent({
/**
* Maps a file entry to its corresponding folder content.
- * @param {FileDto} fileEntry - The file data transfer object to map.
+ * @param {FileTreeDto} fileEntry - The file data transfer object to map.
* @returns {FolderContent | undefined} The corresponding folder content or undefined if the type is unknown.
*/
- mapFileEntryToContent(fileEntry: FileDto): FolderContent | undefined {
+ mapFileEntryToContent(fileEntry: FileTreeDto): FolderContent | undefined {
let content: FolderContent | undefined = undefined;
switch (fileEntry.type) {
case "Tree" as TreeDataType.Tree:
diff --git a/src/modules/resource/components/resource-page/FilesViewHeader.vue b/src/modules/resource/components/resource-page/FilesViewHeader.vue
index 6c2e4079924d7701fc041ea4d691efaabd3604c7..42dd8e9cfe9c0f47c0d4e4e3219dade3d07a20e5 100644
--- a/src/modules/resource/components/resource-page/FilesViewHeader.vue
+++ b/src/modules/resource/components/resource-page/FilesViewHeader.vue
@@ -289,18 +289,17 @@ export default defineComponent({
methods: {
async getGitlabProjectName() {
if (
- this.resource?.type?.options?.gitLabOptions?.repoUrl &&
- this.resource?.type?.options?.gitLabOptions?.accessToken
+ this.resource?.type?.options?.gitLab?.repoUrl &&
+ this.resource?.type?.options?.gitLab?.accessToken
) {
this.gitlabProjects = await this.resourceStore.getGitlabAllProjects(
- this.resource?.type?.options?.gitLabOptions?.repoUrl,
- this.resource?.type?.options?.gitLabOptions?.accessToken
+ this.resource?.type?.options?.gitLab?.repoUrl,
+ this.resource?.type?.options?.gitLab?.accessToken
);
if (this.gitlabProjects) {
this.gitlabInformation.project =
this.gitlabProjects.find(
- (p) =>
- p.id === this.resource?.type?.options?.gitLabOptions?.projectId
+ (p) => p.id === this.resource?.type?.options?.gitLab?.projectId
) ?? null;
}
}
diff --git a/src/modules/resource/components/resource-page/MetadataManager.vue b/src/modules/resource/components/resource-page/MetadataManager.vue
index 7e8a614c9b389df6890a7f4ce23ee21066cd566c..48a335a8c68d4c47de18ead3e5004d53989df38a 100644
--- a/src/modules/resource/components/resource-page/MetadataManager.vue
+++ b/src/modules/resource/components/resource-page/MetadataManager.vue
@@ -61,7 +61,9 @@
<!-- Form Generator -->
<span
v-if="
- resource && resource.applicationProfile !== '' && applicationProfile
+ resource &&
+ resource?.applicationProfile?.uri !== '' &&
+ applicationProfile
"
class="generatedFormSpan"
>
@@ -71,7 +73,7 @@
:fixed-values="resource.fixedValues"
:form-data="currentUsedMetadata"
:locale="$root.$i18n.locale"
- :selected-shape="resource.applicationProfile"
+ :selected-shape="resource.applicationProfile?.uri"
:shapes="applicationProfile"
:disabled-mode="isGuest || resource.archived"
:validation-context="currentFileId + ''"
@@ -665,8 +667,10 @@ export default defineComponent({
// Create the metadata tree DTO and serialize the definition
const metadataTreeForCreationDto: MetadataTreeForCreationDto = {
path: file.path,
- definition: await serializeRDFDefinition(file.metadata, contentType),
- format: contentType as RdfFormat,
+ definition: {
+ content: await serializeRDFDefinition(file.metadata, contentType),
+ type: contentType as RdfFormat,
+ },
};
// Trigger metadata tree creation. Metadata might be already present, so we try updating it if true.
@@ -850,8 +854,10 @@ export default defineComponent({
// Create the metadata tree DTO and serialize the definition
const metadataTreeForUpdateDto: MetadataTreeForUpdateDto = {
path: file.path,
- definition: await serializeRDFDefinition(file.metadata, contentType),
- format: contentType as RdfFormat,
+ definition: {
+ content: await serializeRDFDefinition(file.metadata, contentType),
+ type: contentType as RdfFormat,
+ },
};
const success = await this.resourceStore.updateOrAddMetadataTree(
diff --git a/src/modules/resource/components/resource-page/metadata/MetadataManagerHeader.vue b/src/modules/resource/components/resource-page/metadata/MetadataManagerHeader.vue
index f3923c7d8ef1885bace2c50b6fc70110b124818d..6a484e3a08d3110fd7c6d79e5df76e51d6dad340 100644
--- a/src/modules/resource/components/resource-page/metadata/MetadataManagerHeader.vue
+++ b/src/modules/resource/components/resource-page/metadata/MetadataManagerHeader.vue
@@ -157,17 +157,16 @@ export default defineComponent({
},
},
async mounted() {
- if (this.resource.type?.options?.gitLabOptions && this.isGitLab) {
+ if (this.resource.type?.options?.gitLab && this.isGitLab) {
await this.fetchGitlabBranchInfo();
}
},
methods: {
async fetchGitlabBranchInfo() {
- const projectId = this.resource.type?.options?.gitLabOptions?.projectId;
- const domain = this.resource.type?.options?.gitLabOptions?.repoUrl;
- const accessToken =
- this.resource.type?.options?.gitLabOptions?.accessToken;
- const currentBranch = this.resource.type?.options?.gitLabOptions?.branch;
+ const projectId = this.resource.type?.options?.gitLab?.projectId;
+ const domain = this.resource.type?.options?.gitLab?.repoUrl;
+ const accessToken = this.resource.type?.options?.gitLab?.accessToken;
+ const currentBranch = this.resource.type?.options?.gitLab?.branch;
if (projectId && domain && accessToken && currentBranch) {
const gitlabBranches =
await this.resourceStore.getGitlabBranchesForProject(
diff --git a/src/modules/resource/components/resource-page/resource-type/GitLab.vue b/src/modules/resource/components/resource-page/resource-type/GitLab.vue
index 17e9c61e553f1a6561bb2a7af2360956ad1467d2..1b273ea3e4aeabcd24a080016dd201805c02555f 100644
--- a/src/modules/resource/components/resource-page/resource-type/GitLab.vue
+++ b/src/modules/resource/components/resource-page/resource-type/GitLab.vue
@@ -1,16 +1,16 @@
<template>
<div>
- <li v-if="resource.type?.options?.gitLabOptions?.repoUrl">
+ <li v-if="resource.type?.options?.gitLab?.repoUrl">
<b>{{ `${$t("resourceType.gitlab.domainLabel")} ` }}</b>
- {{ resource.type.options.gitLabOptions.repoUrl }}
+ {{ resource.type.options.gitLab.repoUrl }}
</li>
- <li v-if="resource.type?.options?.gitLabOptions?.projectId">
+ <li v-if="resource.type?.options?.gitLab?.projectId">
<b>{{ `${$t("resourceType.gitlab.projectLabel")} ` }}</b>
{{ gitlabInformation.project ? gitlabInformation.project.name : "" }}
</li>
- <li v-if="resource.type?.options?.gitLabOptions?.branch">
+ <li v-if="resource.type?.options?.gitLab?.branch">
<b>{{ `${$t("resourceType.gitlab.referenceLabel")} ` }}</b>
- {{ resource.type.options.gitLabOptions.branch }}
+ {{ resource.type.options.gitLab.branch }}
</li>
</div>
</template>
@@ -57,18 +57,17 @@ export default defineComponent({
methods: {
async getGitlabProjectName() {
if (
- this.resource?.type?.options?.gitLabOptions?.repoUrl &&
- this.resource?.type?.options?.gitLabOptions?.accessToken
+ this.resource?.type?.options?.gitLab?.repoUrl &&
+ this.resource?.type?.options?.gitLab?.accessToken
) {
this.gitlabProjects = await this.resourceStore.getGitlabAllProjects(
- this.resource?.type?.options?.gitLabOptions?.repoUrl,
- this.resource?.type?.options?.gitLabOptions?.accessToken
+ this.resource?.type?.options?.gitLab?.repoUrl,
+ this.resource?.type?.options?.gitLab?.accessToken
);
if (this.gitlabProjects) {
this.gitlabInformation.project =
this.gitlabProjects.find(
- (p) =>
- p.id === this.resource?.type?.options?.gitLabOptions?.projectId
+ (p) => p.id === this.resource?.type?.options?.gitLab?.projectId
) ?? null;
}
}
diff --git a/src/modules/resource/components/settings/Actions.vue b/src/modules/resource/components/settings/Actions.vue
index 90eee0c02c92c072a8050845f37d5f1c602a840a..632318cecfe484f8e155ab868446abbc5020c686 100644
--- a/src/modules/resource/components/settings/Actions.vue
+++ b/src/modules/resource/components/settings/Actions.vue
@@ -5,7 +5,6 @@
label-for="Archive"
:label="$t('page.settings.actions.resourceArchiveLabel')"
:is-loading="isLoading"
- class="d-flex align-items-center"
info
>
<template #popover>
@@ -34,7 +33,6 @@
label-for="DeleteResource"
:label="$t('page.settings.actions.resourceDeleteLabel')"
:is-loading="isLoading"
- class="d-flex align-items-center"
>
<!-- Delete Button -->
<b-button
diff --git a/src/modules/resource/components/settings/ApplicationProfile.vue b/src/modules/resource/components/settings/ApplicationProfile.vue
index 59386ad5c95ffe97fb9132cc4020e1b1516bd69d..b657bf56b580d0c0aff821c007f88e5cb49a4b65 100644
--- a/src/modules/resource/components/settings/ApplicationProfile.vue
+++ b/src/modules/resource/components/settings/ApplicationProfile.vue
@@ -6,12 +6,12 @@
</b-row>
<FormGenerator
v-else-if="resource"
- :key="resource.applicationProfile"
+ :key="resource.applicationProfile?.uri"
:disabled-mode="!isUserAllowedToEdit || resource.archived"
:fixed-value-mode="true"
:fixed-values="resourceForm.fixedValues"
:locale="$root.$i18n.locale"
- :selected-shape="resource.applicationProfile"
+ :selected-shape="resource.applicationProfile?.uri"
:shapes="applicationProfile"
:class-receiver="getVocabularyInstances"
:user-receiver="async () => user"
diff --git a/src/modules/resource/components/settings/Overview.vue b/src/modules/resource/components/settings/Overview.vue
index a2decb2ecbfe2ded12a0f5e355b1ecabe60bf043..548d5a9ab695a08eac469aa08b7af99c2799b31a 100644
--- a/src/modules/resource/components/settings/Overview.vue
+++ b/src/modules/resource/components/settings/Overview.vue
@@ -58,7 +58,7 @@
<b-form-input
v-if="resource"
id="ResourceNameReadOnly"
- v-model="resource.resourceName"
+ v-model="resource.name"
required
:readonly="readonly"
/>
diff --git a/src/modules/resource/components/settings/resource-type/Generic.vue b/src/modules/resource/components/settings/resource-type/Generic.vue
index 16231a9623879cc42a78c78904ac8a818fbaa594..90ae1a781696e71eaf2f3c7f0da5ebfde83231ad 100644
--- a/src/modules/resource/components/settings/resource-type/Generic.vue
+++ b/src/modules/resource/components/settings/resource-type/Generic.vue
@@ -108,27 +108,27 @@ export default defineComponent({
if (
this.resource?.type?.generalType ===
this.coscineResourceTypes.Rds.General &&
- this.resource?.type?.options?.rdsOptions
+ this.resource?.type?.options?.rds
) {
- return this.resource?.type?.options?.rdsOptions;
+ return this.resource?.type?.options?.rds;
} else if (
this.resource?.type?.generalType ===
this.coscineResourceTypes.RdsS3.General &&
- this.resource?.type?.options?.rdsS3Options
+ this.resource?.type?.options?.rdsS3
) {
- return this.resource?.type?.options?.rdsS3Options;
+ return this.resource?.type?.options?.rdsS3;
} else if (
this.resource?.type?.generalType ===
this.coscineResourceTypes.RdsS3Worm.General &&
- this.resource?.type?.options?.rdsS3WormOptions
+ this.resource?.type?.options?.rdsS3Worm
) {
- return this.resource?.type?.options?.rdsS3WormOptions;
+ return this.resource?.type?.options?.rdsS3Worm;
} else if (
this.resource?.type?.generalType ===
this.coscineResourceTypes.LinkedData.General &&
- this.resource?.type?.options?.linkedDataOptions
+ this.resource?.type?.options?.linkedData
) {
- return this.resource?.type?.options?.linkedDataOptions;
+ return this.resource?.type?.options?.linkedData;
} else return {};
},
},
diff --git a/src/modules/resource/components/settings/resource-type/GitLab.vue b/src/modules/resource/components/settings/resource-type/GitLab.vue
index dfcd4fcb81aca57fde432d7298b47a8530d64ff3..b150e922e496f07c9ddf3aef2a5b78ba404eadd0 100644
--- a/src/modules/resource/components/settings/resource-type/GitLab.vue
+++ b/src/modules/resource/components/settings/resource-type/GitLab.vue
@@ -10,7 +10,7 @@
>
<b-form-input
id="Domain"
- :value="resource?.type?.options?.gitLabOptions?.repoUrl"
+ :value="resource?.type?.options?.gitLab?.repoUrl"
:placeholder="$t('resourceType.gitlab.domain')"
readonly
/>
@@ -26,7 +26,7 @@
>
<b-form-input
id="ProjectId"
- :value="resource?.type?.options?.gitLabOptions?.projectId"
+ :value="resource?.type?.options?.gitLab?.projectId"
:placeholder="$t('resourceType.gitlab.projectId')"
readonly
/>
@@ -276,8 +276,7 @@ export default defineComponent({
this.gitlabInformation.project =
this.gitlabProjects.find(
- (p) =>
- p.id === this.resource?.type?.options?.gitLabOptions?.projectId
+ (p) => p.id === this.resource?.type?.options?.gitLab?.projectId
) ?? null;
await this.setSelectedGitLabProject();
if (this.gitlabBranches) {
@@ -365,12 +364,12 @@ export default defineComponent({
async initTabContent() {
this.isLoading = true;
if (
- this.resource?.type?.options?.gitLabOptions?.repoUrl &&
+ this.resource?.type?.options?.gitLab?.repoUrl &&
this.resourceForUpdate.resourceTypeOptions?.gitlabResourceTypeOptions
) {
// A resource type has been selected, but the resource's properties are unset. Set the values.
this.gitlabInformation.domain =
- this.resource.type.options?.gitLabOptions.repoUrl;
+ this.resource.type.options?.gitLab.repoUrl;
this.gitlabInformation.accessToken =
this.resourceForUpdate.resourceTypeOptions.gitlabResourceTypeOptions.accessToken;
await this.verifyDomainAndToken(true);
diff --git a/src/modules/resource/pages/CreateResource.vue b/src/modules/resource/pages/CreateResource.vue
index a09ca977129a00731b0c7719712646dbaec02bb9..652da67569d062fab519027fd22261b4cf59ddc0 100644
--- a/src/modules/resource/pages/CreateResource.vue
+++ b/src/modules/resource/pages/CreateResource.vue
@@ -112,12 +112,12 @@ export default defineComponent({
description: "",
displayName: "",
fixedValues: {},
- resourceName: "",
+ name: "",
keywords: [] as string[],
usageRights: "",
disciplines: [] as DisciplineForResourceManipulationDto[],
visibility: {} as VisibilityForResourceManipulationDto,
- applicationProfile: "",
+ applicationProfile: { uri: "" },
resourceTypeId: "",
resourceTypeOptions: {} as ResourceTypeOptionsForCreationDto,
} as ResourceForCreationDto,
@@ -225,7 +225,7 @@ export default defineComponent({
this.isLoadingFormGenerator = true;
const applicationProfile =
await this.resourceStore.getApplicationProfile(
- this.resourceForCreation.applicationProfile
+ this.resourceForCreation.applicationProfile.uri
);
this.applicationProfileDefinition = applicationProfile;
this.isLoadingFormGenerator = false;
diff --git a/src/modules/resource/pages/ResourcePage.spec.ts b/src/modules/resource/pages/ResourcePage.spec.ts
index 8b4ceb1a4693bf23f6e8b7cddd938c2a95a075c1..5a218ccc6030a903db7c524770fd4e2182929386 100644
--- a/src/modules/resource/pages/ResourcePage.spec.ts
+++ b/src/modules/resource/pages/ResourcePage.spec.ts
@@ -20,7 +20,7 @@ import { routes } from "@/router";
import Vue from "vue";
-import { getTestUserState } from "@/data/mockup/testUser";
+import { getTestShibbolethUserState } from "@/data/mockup/testUser";
import { getTestResourceState } from "@/data/mockup/testResource";
import { testProjectState } from "@/data/mockup/testProject";
import useResourceStore from "../store";
@@ -53,7 +53,7 @@ describe("ResourcePage.vue", async () => {
initialState: {
project: testProjectState,
resource: await getTestResourceState(),
- user: getTestUserState(),
+ user: getTestShibbolethUserState(),
},
});
diff --git a/src/modules/resource/pages/Settings.vue b/src/modules/resource/pages/Settings.vue
index 07a843bfc430aa416d6309d6659ca110beaf6bba..25baff464be52a7a333a98f6e3bb57ee0b028ff1 100644
--- a/src/modules/resource/pages/Settings.vue
+++ b/src/modules/resource/pages/Settings.vue
@@ -134,7 +134,7 @@ export default defineComponent({
resourceForUpdate: {
description: "",
displayName: "",
- resourceName: "",
+ name: "",
keywords: [] as string[],
license: {} as LicenseForResourceManipulationDto,
usageRights: "",
@@ -168,15 +168,15 @@ export default defineComponent({
| undefined {
switch (this.resource?.type?.generalType) {
case this.coscineResourceTypes.LinkedData.General:
- return this.resource.type.options?.linkedDataOptions;
+ return this.resource.type.options?.linkedData;
case this.coscineResourceTypes.Gitlab.General:
- return this.resource.type.options?.gitLabOptions;
+ return this.resource.type.options?.gitLab;
case this.coscineResourceTypes.Rds.General:
- return this.resource.type.options?.rdsOptions;
+ return this.resource.type.options?.rds;
case this.coscineResourceTypes.RdsS3.General:
- return this.resource.type.options?.rdsS3Options;
+ return this.resource.type.options?.rdsS3;
case this.coscineResourceTypes.RdsS3Worm.General:
- return this.resource.type.options?.rdsS3WormOptions;
+ return this.resource.type.options?.rdsS3Worm;
default:
return undefined;
}
@@ -331,7 +331,7 @@ export default defineComponent({
this.isLoading = true;
const applicationProfile =
await this.resourceStore.getApplicationProfile(
- this.resource.applicationProfile
+ this.resource.applicationProfile.uri ?? ""
);
this.applicationProfile = applicationProfile;
this.isLoading = false;
diff --git a/src/modules/resource/store.ts b/src/modules/resource/store.ts
index 2e8398d8ad83708d7b557186f5ec20187df13542..049a1b3a7f55d603c5496dc0502f91a29228e1e2 100644
--- a/src/modules/resource/store.ts
+++ b/src/modules/resource/store.ts
@@ -37,8 +37,8 @@ import type {
ResourceForUpdateDto,
ResourceQuotaDto,
AcceptedLanguage,
- FileDto,
- MetadataDto,
+ FileTreeDto,
+ MetadataTreeDto,
} from "@coscine/api-client/dist/types/Coscine.Api/api";
import { wrapListRequest } from "@/util/wrapListRequest";
/*
@@ -153,24 +153,24 @@ export const useResourceStore = defineStore({
async retrieveApplicationProfile(resource: VisitedResourceObject) {
const notificationStore = useNotificationStore();
try {
- if (resource.applicationProfile) {
+ if (resource.applicationProfile?.uri) {
const apiResponse = await ApplicationProfileApi.getApplicationProfile(
- resource.applicationProfile,
+ resource.applicationProfile.uri,
"JsonLd" as RdfFormat
);
const returnedData = apiResponse.data.data;
if (
- returnedData?.definition &&
- returnedData?.format &&
- returnedData?.baseUri
+ returnedData?.definition?.content &&
+ returnedData?.definition?.type &&
+ returnedData?.uri
) {
resource.rawApplicationProfile = await parseRDFDefinition(
- returnedData.definition,
- returnedData.format,
- returnedData.baseUri
+ returnedData.definition.content,
+ returnedData.definition.type,
+ returnedData.uri
);
resource.fullApplicationProfile = await resolveImports(
- returnedData.baseUri,
+ returnedData.uri,
resource.rawApplicationProfile
);
} else {
@@ -197,18 +197,18 @@ export const useResourceStore = defineStore({
);
const returnedData = apiResponse.data.data;
if (
- returnedData?.definition &&
- returnedData?.format &&
- returnedData?.baseUri
+ returnedData?.definition?.content &&
+ returnedData?.definition?.type &&
+ returnedData?.uri
) {
let returnApplicationProfile = await parseRDFDefinition(
- returnedData.definition,
- returnedData.format,
- returnedData.baseUri
+ returnedData.definition.content,
+ returnedData.definition.type,
+ returnedData.uri
);
if (doResolveImports) {
returnApplicationProfile = await resolveImports(
- returnedData.baseUri,
+ returnedData.uri,
returnApplicationProfile
);
}
@@ -608,7 +608,7 @@ export const useResourceStore = defineStore({
resourceId: string,
filePath: string,
format?: RdfFormat
- ): Promise<MetadataDto[] | null | undefined> {
+ ): Promise<MetadataTreeDto[] | null | undefined> {
const notificationStore = useNotificationStore();
try {
const response = await TreeApi.getMetadataTree(
@@ -629,7 +629,7 @@ export const useResourceStore = defineStore({
projectId: string,
resourceId: string,
filePath: string
- ): Promise<FileDto[] | null | undefined> {
+ ): Promise<FileTreeDto[] | null | undefined> {
const notificationStore = useNotificationStore();
try {
const response = await TreeApi.getFileTree(
diff --git a/src/modules/resource/utils/MetadataManagerUtil.ts b/src/modules/resource/utils/MetadataManagerUtil.ts
index c49ef45e1c2a5c8eb2f4cc6345a1e6b067c94176..21b403a89e4bd19ecd4b5a19bfdae9658e12a1cf 100644
--- a/src/modules/resource/utils/MetadataManagerUtil.ts
+++ b/src/modules/resource/utils/MetadataManagerUtil.ts
@@ -40,10 +40,13 @@ export default {
);
// If the metadata exists and has a definition, parse it
- if (metadataTree?.[0]?.definition && metadataTree[0].format) {
+ if (
+ metadataTree?.[0]?.definition?.content &&
+ metadataTree[0].definition.type
+ ) {
return await parseRDFDefinition(
- metadataTree[0].definition,
- metadataTree[0].format
+ metadataTree[0].definition.content,
+ metadataTree[0].definition.type
);
}
diff --git a/src/modules/resource/utils/linkedData.ts b/src/modules/resource/utils/linkedData.ts
index c0632b8abfb61ecb0871340f3e31aa496472d81b..76d08fc98ffd286e8c94c684947932486d8b2a3a 100644
--- a/src/modules/resource/utils/linkedData.ts
+++ b/src/modules/resource/utils/linkedData.ts
@@ -87,10 +87,13 @@ export async function resolveImports(
"JsonLd" as RdfFormat
);
const apResponse = importedApiResponse.data.data;
- if (apResponse?.definition && apResponse?.format) {
+ if (
+ apResponse?.definition?.content &&
+ apResponse?.definition.type
+ ) {
const importedApplicationProfile = await parseRDFDefinition(
- apResponse.definition,
- apResponse.format,
+ apResponse.definition.content,
+ apResponse.definition.type,
importedAP.value
);
fullApplicationProfile = (
diff --git a/src/modules/resource/utils/validators.ts b/src/modules/resource/utils/validators.ts
index ab58e281ae0893f62d84cd52b1fbd0ed5f4eb815..0ba1ba78e9c03741bcbfd134322391bab4b5161b 100644
--- a/src/modules/resource/utils/validators.ts
+++ b/src/modules/resource/utils/validators.ts
@@ -80,8 +80,8 @@ const hasValidResourceTypeOptionsWithQuota = (
| RdsS3ResourceTypeOptionsForManipulationDto
| RdsS3WormResourceTypeOptionsForManipulationDto
): boolean => {
- if (options !== undefined && options.size) {
- const { value, unit } = options.size;
+ if (options !== undefined && options.quota) {
+ const { value, unit } = options.quota;
const isValidUnit = Object.values(QuotaUnit).includes(unit as QuotaUnit);
const isValidValue = Number.isFinite(value) && value > 0;
return isValidUnit && isValidValue;
diff --git a/src/modules/user/UserModule.vue b/src/modules/user/UserModule.vue
index 1d1a3add9b9a2d5d7053005cf20fe40ac423bedc..3eae27543ef74638810f448468aecb2691890658 100644
--- a/src/modules/user/UserModule.vue
+++ b/src/modules/user/UserModule.vue
@@ -52,5 +52,3 @@ export default defineComponent({
},
});
</script>
-
-<style></style>
diff --git a/src/modules/user/i18n/de.ts b/src/modules/user/i18n/de.ts
index 5eecc836a628f786809e5b5ef480be65c1069c34..c009f0c91efd914790d54c34186c58753202d4e1 100644
--- a/src/modules/user/i18n/de.ts
+++ b/src/modules/user/i18n/de.ts
@@ -15,8 +15,8 @@ export default {
header: "Persönliche Daten",
title: "Titel",
- firstName: "Vorname",
- lastName: "Name",
+ givenName: "Vorname",
+ familyName: "Nachname",
email: "E-Mail",
organization: "Organisation",
institute: "Institut",
@@ -41,15 +41,17 @@ export default {
"Keine Ergebnisse verfügbar. Bitte passen Sie die Suchanfrage an.",
noOptions: "Die Liste ist leer.",
noOptionsOrganization: "Suche nach Organisationen",
+ searchNotEnoughCharacters:
+ "Bitte geben Sie mindestens {min} Zeichen ein, um zu suchen.",
},
labels: {
titleLabel:
"@:(page.userprofile.form.personalInformation.title)@:(page.userprofile.form.labelSymbol)",
firstNameLabel:
- "@:(page.userprofile.form.personalInformation.firstName)@:(page.userprofile.form.labelSymbol)",
+ "@:(page.userprofile.form.personalInformation.givenName)@:(page.userprofile.form.labelSymbol)",
lastNameLabel:
- "@:(page.userprofile.form.personalInformation.lastName)@:(page.userprofile.form.labelSymbol)",
+ "@:(page.userprofile.form.personalInformation.familyName)@:(page.userprofile.form.labelSymbol)",
emailLabel:
"@:(page.userprofile.form.personalInformation.email)@:(page.userprofile.form.labelSymbol)",
organizationLabel:
diff --git a/src/modules/user/i18n/en.ts b/src/modules/user/i18n/en.ts
index b36d6be9b678ec66b7145c2ada202932173508da..8ae1524aee7c4dd656995fbbe2c0faa4cefe889a 100644
--- a/src/modules/user/i18n/en.ts
+++ b/src/modules/user/i18n/en.ts
@@ -15,8 +15,8 @@ export default {
header: "Personal Information",
title: "Title",
- firstName: "First Name",
- lastName: "Last Name",
+ givenName: "Given Name",
+ familyName: "Family Name",
email: "Email",
organization: "Organization",
institute: "Institute",
@@ -39,15 +39,17 @@ export default {
noResults: "No elements found. Consider changing the search query.",
noOptions: "List is empty.",
noOptionsOrganization: "Search for organizations",
+ searchNotEnoughCharacters:
+ "Please enter at least {min} characters to search.",
},
labels: {
titleLabel:
"@:(page.userprofile.form.personalInformation.title)@:(page.userprofile.form.labelSymbol)",
firstNameLabel:
- "@:(page.userprofile.form.personalInformation.firstName)@:(page.userprofile.form.labelSymbol)",
+ "@:(page.userprofile.form.personalInformation.givenName)@:(page.userprofile.form.labelSymbol)",
lastNameLabel:
- "@:(page.userprofile.form.personalInformation.lastName)@:(page.userprofile.form.labelSymbol)",
+ "@:(page.userprofile.form.personalInformation.familyName)@:(page.userprofile.form.labelSymbol)",
emailLabel:
"@:(page.userprofile.form.personalInformation.email)@:(page.userprofile.form.labelSymbol)",
organizationLabel:
diff --git a/src/modules/user/pages/UserProfile.spec.ts b/src/modules/user/pages/UserProfile.spec.ts
index b40611347ed6b90485fbca07a2f7fd62a99fafeb..e8b54c116e341e3d79142ac6eaff2ae41a712b7f 100644
--- a/src/modules/user/pages/UserProfile.spec.ts
+++ b/src/modules/user/pages/UserProfile.spec.ts
@@ -1,5 +1,5 @@
/* Testing imports */
-import { createLocalVue, mount } from "@vue/test-utils";
+import { type Wrapper, createLocalVue, mount } from "@vue/test-utils";
import { createTestingPinia } from "@pinia/testing";
/* Vue i18n */
@@ -23,122 +23,181 @@ import Multiselect from "@/plugins/deprecated/Multiselect.vue";
import UserProfile from "./UserProfile.vue";
import MergeUserModal from "./modals/MergeUserModal.vue";
import {
- testInstitute,
- testOrganization,
- getTestUserState,
+ testInstituteFromShibboleth,
+ testOrganizationFromShibboleth,
+ getTestShibbolethUserState,
+ getTestOrcidUserState,
} from "@/data/mockup/testUser";
-import useUserStore from "@/modules/user/store";
import type Vue from "vue";
+import type { Validation, ValidationArgs } from "@vuelidate/core";
import useLoginStore from "@/modules/login/store";
+import { UserForUpdateDto } from "@coscine/api-client/dist/types/Coscine.Api";
/* Create a local Vue instance */
const localVue = createLocalVue();
localVue.use(PiniaVuePlugin);
localVue.use(BootstrapVue);
+// Define the Vue instance type (computed properties)
+interface UserProfileComponent extends Vue {
+ v$: Validation<ValidationArgs<{ userForUpdate: UserForUpdateDto }>, unknown>;
+}
+
describe("UserProfile.vue", () => {
localVue.component("Multiselect", Multiselect);
+ let wrapper: Wrapper<UserProfileComponent>;
- /* Checks for correct button validation for external users */
- test("externalUser", async () => {
- const testUserState = getTestUserState();
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
- testUserState.user!.organizations = [];
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
- testUserState.user!.institutes = [];
- const wrapper = mount(UserProfile as unknown as typeof Vue, {
- pinia: createTestingPinia({
- createSpy: vitest.fn,
- initialState: {
- user: testUserState,
- },
- }),
+ /* Checks for correct button validation for ORCiD (external) users */
+ test("ORCiD (external) User", async () => {
+ // Prepare the initial state of the user store
+ const testUserState = getTestOrcidUserState();
+ if (!testUserState.user) {
+ throw new Error("Test user is null or undefined!");
+ }
+ // Clear the organizations and institutes to test the validation
+ testUserState.user.organizations = [];
+ testUserState.user.institutes = [];
+ // Create a mocked pinia instance with initial state
+ const testingPinia = createTestingPinia({
+ createSpy: vitest.fn,
+ initialState: {
+ user: testUserState,
+ },
+ });
+ // Mount the component
+ wrapper = mount(UserProfile as unknown as typeof Vue, {
+ pinia: testingPinia,
i18n,
localVue,
- });
+ }) as Wrapper<UserProfileComponent>;
- expect(wrapper.get("#saveBtn").attributes()["disabled"]).toBe("disabled");
+ /*
+ * NOTE: This test might fail if any of the other validation property is invalid!
+ * Ensure here that the rest of the properties inside the validation v$ are valid.
+ */
+ expect(wrapper.vm.v$.userForUpdate.organization.$invalid).toBeTruthy();
+ expect(wrapper.vm.v$.userForUpdate.institute.$invalid).toBeTruthy();
+ // Save button should be disabled, since neither organization nor institute is set
+ expect(
+ (wrapper.get("#saveBtn").element as HTMLButtonElement).disabled
+ ).toBeTruthy();
/* Organization */
const element = wrapper.findComponent({
ref: "organization",
});
expect(element.exists()).toBeTruthy();
- (element.vm as unknown as typeof Multiselect).select(testOrganization.name);
+ (element.vm as unknown as typeof Multiselect).select(
+ testOrganizationFromShibboleth.displayName
+ );
expect(wrapper.vm.$data.userForUpdate.organization).toBe(
- testOrganization.name
+ testOrganizationFromShibboleth.displayName
);
- expect(wrapper.get("#saveBtn").attributes()["disabled"]).toBe("disabled");
+ expect(wrapper.vm.v$.userForUpdate.organization.$invalid).toBeFalsy();
+ expect(wrapper.vm.v$.userForUpdate.institute.$invalid).toBeTruthy();
+ expect(
+ (wrapper.get("#saveBtn").element as HTMLButtonElement).disabled
+ ).toBeTruthy();
/* Institute */
await wrapper.get("#institute").setValue("Test Institute");
await wrapper.vm.$nextTick();
expect(wrapper.vm.$data.userForUpdate.institute).toBe("Test Institute");
- /* Active Save Button since every condition is fulfilled */
- expect(wrapper.get("#saveBtn").attributes()["disabled"]).not.toBe(
- "disabled"
- );
+ /* Active Save Button since every condition is fulfilled and the entire form is valid */
+ expect(wrapper.vm.v$.userForUpdate.organization.$invalid).toBeFalsy();
+ expect(wrapper.vm.v$.userForUpdate.institute.$invalid).toBeFalsy();
+ expect(
+ (wrapper.get("#saveBtn").element as HTMLButtonElement).disabled
+ ).toBeFalsy();
});
/* Checks for correct button validation for internal users with only organization */
- test("internalUserOnlyOrg", async () => {
- const testUserState = getTestUserState();
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
- testUserState.user!.institutes = [];
- const wrapper = mount(UserProfile as unknown as typeof Vue, {
- pinia: createTestingPinia({
- createSpy: vitest.fn,
- initialState: {
- user: testUserState,
- },
- }),
+ test("Shibboleth (internal) User with only an organization set", async () => {
+ // Prepare the initial state of the user store
+ const testUserState = getTestShibbolethUserState();
+ if (!testUserState.user) {
+ throw new Error("Test user is null or undefined!");
+ }
+ // Clear the organizations and institutes to test the validation
+ testUserState.user.institutes = [];
+ // Create a mocked pinia instance with initial state
+ const testingPinia = createTestingPinia({
+ createSpy: vitest.fn,
+ initialState: {
+ user: testUserState,
+ },
+ });
+ // Mount the component
+ wrapper = mount(UserProfile as unknown as typeof Vue, {
+ pinia: testingPinia,
i18n,
localVue,
- });
+ }) as Wrapper<UserProfileComponent>;
- /* Save button disabled, since institution is not set */
- expect(wrapper.get("#saveBtn").attributes()["disabled"]).toBe("disabled");
+ /*
+ * NOTE: This test might fail if any of the other validation property is invalid!
+ * Ensure here that the rest of the properties inside the validation v$ are valid.
+ */
+ expect(wrapper.vm.v$.userForUpdate.organization.$invalid).toBeFalsy();
+ expect(wrapper.vm.v$.userForUpdate.institute.$invalid).toBeTruthy();
+ // Save button should be disabled, since institute is not set
+ expect(
+ (wrapper.get("#saveBtn").element as HTMLButtonElement).disabled
+ ).toBeTruthy();
- /* Organization */
- const element = wrapper.findComponent({
+ /* Organization dropdown */
+ const organizationDropdown = wrapper.findComponent({
ref: "organization",
});
- expect(element.exists()).toBeTruthy();
- expect((element.vm as unknown as typeof Multiselect).$props.disabled).toBe(
- true
- );
+ expect(organizationDropdown.exists()).toBeTruthy();
+ // Shibboleth users' organization is always read-only, thus the dropdown should be disabled
+ expect(
+ (organizationDropdown.vm as unknown as typeof Multiselect).$props.disabled
+ ).toBeTruthy();
- /* Institute */
+ /* Institute field */
await wrapper.get("#institute").setValue("Test Institute");
await wrapper.vm.$nextTick();
expect(wrapper.vm.$data.userForUpdate.institute).toBe("Test Institute");
- /* Active Save Button since every condition is fulfilled */
- expect(wrapper.get("#saveBtn").attributes()["disabled"]).not.toBe(
- "disabled"
- );
+ /* Active Save Button since every condition is fulfilled and the entire form is valid */
+ expect(wrapper.vm.v$.userForUpdate.organization.$invalid).toBeFalsy();
+ expect(wrapper.vm.v$.userForUpdate.institute.$invalid).toBeFalsy();
+ expect(
+ (wrapper.get("#saveBtn").element as HTMLButtonElement).disabled
+ ).toBeFalsy();
});
/* Checks for correct button validation for internal users */
- test("internalUser", async () => {
- const testUserState = getTestUserState();
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
- testUserState.user!.organizations = [testOrganization, testInstitute];
- const wrapper = mount(UserProfile as unknown as typeof Vue, {
- pinia: createTestingPinia({
- createSpy: vitest.fn,
- initialState: {
- user: testUserState,
- },
- }),
+ test("Shibboleth (internal) User email changed", async () => {
+ // Prepare the initial state of the user store
+ const testUserState = getTestShibbolethUserState();
+ if (!testUserState.user) {
+ throw new Error("Test user is null or undefined!");
+ }
+ // Create a mocked pinia instance with initial state
+ const testingPinia = createTestingPinia({
+ createSpy: vitest.fn,
+ initialState: {
+ user: testUserState,
+ },
+ });
+ // Mount the component
+ wrapper = mount(UserProfile as unknown as typeof Vue, {
+ pinia: testingPinia,
i18n,
localVue,
- });
+ }) as Wrapper<UserProfileComponent>;
/* Save button disabled, since nothing has changed */
- expect(wrapper.get("#saveBtn").attributes()["disabled"]).toBe("disabled");
+ expect(wrapper.vm.v$.userForUpdate.email.$invalid).toBeFalsy();
+ expect(wrapper.vm.v$.userForUpdate.organization.$invalid).toBeFalsy();
+ expect(wrapper.vm.v$.userForUpdate.institute.$invalid).toBeFalsy();
+ expect(
+ (wrapper.get("#saveBtn").element as HTMLButtonElement).disabled
+ ).toBeTruthy();
/* Organization */
const element = wrapper.findComponent({
@@ -150,25 +209,30 @@ describe("UserProfile.vue", () => {
);
/* Email */
- const userStore = useUserStore();
- expect(userStore.user?.email).toBe("example@example.com");
- expect(wrapper.vm.$data.userForUpdate.email).toBe("example@example.com");
+ expect(wrapper.vm.$data.userForUpdate.email).toBe("example@university.com");
+ expect(wrapper.vm.v$.userForUpdate.email.$invalid).toBeFalsy();
+ expect(wrapper.vm.v$.userForUpdate.email.$anyDirty).toBeFalsy();
await wrapper.get("#email").setValue("servicedesk@itc.rwth-aachen.de");
expect(wrapper.vm.$data.userForUpdate.email).toBe(
"servicedesk@itc.rwth-aachen.de"
);
+ expect(wrapper.vm.v$.userForUpdate.email.$invalid).toBeFalsy();
+ expect(wrapper.vm.v$.userForUpdate.email.$anyDirty).toBeTruthy();
/* Active Save Button since every condition is fulfilled */
- expect(wrapper.get("#saveBtn").attributes()["disabled"]).not.toBe(
- "disabled"
- );
+ expect(
+ (wrapper.get("#saveBtn").element as HTMLButtonElement).disabled
+ ).toBeFalsy();
});
/* Checks for correct ORCID connect button behavior */
- test("orcidConnection", async () => {
- const testUserState = getTestUserState();
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
- testUserState.user!.organizations = [testOrganization, testInstitute];
+ test("ORCID connect button triggers user merge modal and logout action", async () => {
+ const testUserState = getTestShibbolethUserState();
+ if (!testUserState.user) {
+ throw new Error("Test user is null or undefined!");
+ }
+ testUserState.user.organizations = [testOrganizationFromShibboleth];
+ testUserState.user.institutes = [testInstituteFromShibboleth];
/* Assign the test pinia store to a variable to use later */
const testingPinia = createTestingPinia({
createSpy: vitest.fn,
@@ -193,7 +257,7 @@ describe("UserProfile.vue", () => {
// Assert modal appeared
const modal = wrapper.findComponent(MergeUserModal);
- expect(modal.exists()).toBe(true);
+ expect(modal.exists()).toBeTruthy();
// Find and click logout button
const logoutBtn = modal.find("#logOutForMerge");
diff --git a/src/modules/user/pages/UserProfile.vue b/src/modules/user/pages/UserProfile.vue
index db5e1616dfd8015df00261fdd62b0490a80ccbb0..a99c685a8deb819fb3b54a044e56ae7fa9ee525d 100644
--- a/src/modules/user/pages/UserProfile.vue
+++ b/src/modules/user/pages/UserProfile.vue
@@ -57,7 +57,7 @@
<!-- First Name -->
<CoscineFormGroup
:mandatory="true"
- label-for="firstName"
+ label-for="givenName"
:label="
$t(
'page.userprofile.form.personalInformation.labels.firstNameLabel'
@@ -67,15 +67,15 @@
type="input"
>
<b-form-input
- id="firstName"
- v-model="v$.userForUpdate.firstName.$model"
+ id="givenName"
+ v-model="v$.userForUpdate.givenName.$model"
:state="
- v$.userForUpdate.firstName.$dirty
- ? !v$.userForUpdate.firstName.$invalid
+ v$.userForUpdate.givenName.$dirty
+ ? !v$.userForUpdate.givenName.$invalid
: null
"
:placeholder="
- $t('page.userprofile.form.personalInformation.firstName')
+ $t('page.userprofile.form.personalInformation.givenName')
"
/>
</CoscineFormGroup>
@@ -83,7 +83,7 @@
<!-- Last Name -->
<CoscineFormGroup
:mandatory="true"
- label-for="lastName"
+ label-for="familyName"
:label="
$t('page.userprofile.form.personalInformation.labels.lastNameLabel')
"
@@ -91,15 +91,15 @@
type="input"
>
<b-form-input
- id="lastName"
- v-model="v$.userForUpdate.lastName.$model"
+ id="familyName"
+ v-model="v$.userForUpdate.familyName.$model"
:state="
- v$.userForUpdate.lastName.$dirty
- ? !v$.userForUpdate.lastName.$invalid
+ v$.userForUpdate.familyName.$dirty
+ ? !v$.userForUpdate.familyName.$invalid
: null
"
:placeholder="
- $t('page.userprofile.form.personalInformation.lastName')
+ $t('page.userprofile.form.personalInformation.familyName')
"
/>
</CoscineFormGroup>
@@ -114,17 +114,21 @@
:is-loading="isLoading"
type="input"
>
- <b-form-input
- id="email"
- v-model="v$.userForUpdate.email.$model"
- :state="
- v$.userForUpdate.email.$dirty || !userForUpdate.email
- ? !v$.userForUpdate.email.$invalid
- : null
- "
- :placeholder="$t('page.userprofile.form.personalInformation.email')"
- />
- <div id="emailHint">{{ emailHint }}</div>
+ <div class="w-100">
+ <b-form-input
+ id="email"
+ v-model="v$.userForUpdate.email.$model"
+ :state="
+ v$.userForUpdate.email.$dirty || !userForUpdate.email
+ ? !v$.userForUpdate.email.$invalid
+ : null
+ "
+ :placeholder="
+ $t('page.userprofile.form.personalInformation.email')
+ "
+ />
+ <div id="emailHint">{{ emailHint }}</div>
+ </div>
</CoscineFormGroup>
<!-- Organization -->
@@ -147,7 +151,7 @@
v-model="v$.userForUpdate.organization.$model"
:options="ror"
:multiple="false"
- :loading="loadingOrganizations"
+ :loading="isLoadingOrganizations"
:show-labels="false"
:placeholder="
$t(
@@ -156,12 +160,31 @@
"
@search-change="triggerFetchOptions"
>
- <template slot="noOptions">
- {{
- $t(
- "page.userprofile.form.personalInformation.multiselect.noOptionsOrganization"
- )
- }}
+ <template #noResult="props">
+ <span v-if="props.search.length < searchCharThreshold">
+ {{
+ $t(
+ "page.userprofile.form.personalInformation.multiselect.searchNotEnoughCharacters",
+ { min: searchCharThreshold }
+ )
+ }}
+ </span>
+ <span v-else>
+ {{
+ $t(
+ "page.userprofile.form.personalInformation.multiselect.noResults"
+ )
+ }}
+ </span>
+ </template>
+ <template #noOptions>
+ <span v-if="!isLoadingOrganizations">
+ {{
+ $t(
+ "page.userprofile.form.personalInformation.multiselect.noOptionsOrganization"
+ )
+ }}
+ </span>
</template>
</multiselect>
@@ -318,10 +341,9 @@
>
<b-button
variant="secondary"
- class="float-left"
name="orcidConnect"
:disabled="orcidConnected"
- @click.prevent="clickConnect(externalAuthenticators.OrciD)"
+ @click.prevent="clickOrcidConnect()"
>
<img
class="d-inline mr-1"
@@ -345,10 +367,9 @@
>
<b-button
variant="secondary"
- class="float-left"
name="shibbolethConnect"
:disabled="shibbolethConnected"
- @click.prevent="clickConnect(externalAuthenticators.Shibboleth)"
+ @click.prevent="clickSsoConnect()"
>
{{
shibbolethConnected
@@ -365,7 +386,7 @@
ref="saveBtn"
type="submit"
variant="primary"
- class="float-right"
+ class="ml-auto"
name="save"
:disabled="
v$.userForUpdate.$invalid ||
@@ -401,12 +422,12 @@ import useLoginStore from "@/modules/login/store";
import useNotificationStore from "@/store/notification";
import type {
DisciplineDto,
+ IdentityProviders,
LanguageDto,
TitleDto,
UserDto,
UserForUpdateDto,
} from "@coscine/api-client/dist/types/Coscine.Api/api";
-import { ExternalAuthenticators } from "../types";
import { UserDto2UserForUpdateDto, userMapper } from "@/mapping/user";
export default defineComponent({
@@ -424,22 +445,22 @@ export default defineComponent({
return {
userForUpdate: {
title: {}, // This needs to be nullified for the multiselect's placeholder
- firstName: "",
- lastName: "",
+ givenName: "",
+ familyName: "",
email: "",
organization: "",
institute: "",
disciplines: [] as DisciplineDto[],
language: {},
} as UserForUpdateDto,
+ searchCharThreshold: 3,
isLoading: false,
savingProfile: false,
currentUserComponent: "UserProfileComponent",
selectedExternalOrganization: null as Record<string, unknown> | null,
queryTimer: 0,
- loadingOrganizations: false,
- externalAuthenticators: ExternalAuthenticators,
- providerToMerge: null as ExternalAuthenticators | null,
+ isLoadingOrganizations: false,
+ providerToMerge: null as IdentityProviders | null,
};
},
computed: {
@@ -460,7 +481,7 @@ export default defineComponent({
return this.$t(
"page.userprofile.form.personalInformation.emailChange.noAddress"
).toString();
- } else if (!this.user?.isEmailConfirmed) {
+ } else if (!this.user?.emails?.some((e) => e.isConfirmed)) {
return this.$t(
"page.userprofile.form.personalInformation.emailChange.pendingConfirmation"
).toString();
@@ -470,7 +491,7 @@ export default defineComponent({
institutes(): string[] {
if (this.user?.institutes) {
return this.user.institutes
- .map((org) => (org.name ? org.name : "")) // Extract organization display name, could contain empty strings
+ .map((org) => (org.displayName ? org.displayName : "")) // Extract organization display name, could contain empty strings
.filter((n) => n); // Filter out empty strings, if any;;
}
return [];
@@ -479,11 +500,11 @@ export default defineComponent({
return this.userStore.userProfile.languages;
},
orcidConnected(): boolean {
- if (this.user?.externalAuthenticators) {
- return this.user.externalAuthenticators.some(
- (externalAuthenticator) =>
- externalAuthenticator.displayName &&
- externalAuthenticator.displayName.toLowerCase() === "orcid"
+ if (this.user?.identities) {
+ return this.user.identities.some(
+ (idProvider) =>
+ idProvider.displayName &&
+ idProvider.displayName.toLowerCase() === "orcid"
);
}
return false;
@@ -491,7 +512,7 @@ export default defineComponent({
organizations(): string[] {
if (this.user?.organizations) {
return this.user?.organizations
- .map((org) => (org.name ? org.name : "")) // Extract organization display name, could contain empty strings
+ .map((org) => (org.displayName ? org.displayName : "")) // Extract organization display name, could contain empty strings
.filter((n) => n); // Filter out empty strings, if any;
}
return [];
@@ -506,18 +527,18 @@ export default defineComponent({
const organizations = this.userStore.userProfile.organizations;
if (organizations) {
return organizations
- .map((org) => (org.name ? org.name : "")) // Extract organization display name, could contain empty strings
+ .map((org) => (org.displayName ? org.displayName : "")) // Extract organization display name, could contain empty strings
.filter((n) => n); // Filter out empty strings, if any
} else {
return [];
}
},
shibbolethConnected(): boolean {
- if (this.user?.externalAuthenticators) {
- return this.user?.externalAuthenticators.some(
- (externalAuthenticator) =>
- externalAuthenticator.displayName &&
- externalAuthenticator.displayName.toLowerCase() === "shibboleth"
+ if (this.user?.identities) {
+ return this.user?.identities.some(
+ (idProvider) =>
+ idProvider.displayName &&
+ idProvider.displayName.toLowerCase() === "shibboleth"
);
}
return false;
@@ -539,8 +560,8 @@ export default defineComponent({
return {
userForUpdate: {
title: {},
- firstName: { required },
- lastName: { required },
+ givenName: { required },
+ familyName: { required },
email: { email, required },
organization: { required, organizationValidator },
institute: { required, instituteValidator },
@@ -573,7 +594,13 @@ export default defineComponent({
}
}
},
- async clickConnect(provider: ExternalAuthenticators) {
+ async clickOrcidConnect() {
+ await this.clickConnect("OrciD" as IdentityProviders.OrciD);
+ },
+ async clickSsoConnect() {
+ await this.clickConnect("Shibboleth" as IdentityProviders.Shibboleth);
+ },
+ async clickConnect(provider: IdentityProviders) {
this.providerToMerge = provider;
this.$bvModal.show("mergeUserModal");
},
@@ -603,18 +630,18 @@ export default defineComponent({
},
triggerFetchOptions(search: string) {
clearTimeout(this.queryTimer);
- if (search.length < 3) {
+ if (search.length < this.searchCharThreshold) {
return;
}
+ this.isLoadingOrganizations = true; // Set the flag here, to avoid delayed loading indication
this.queryTimer = window.setTimeout(
() => this.fetchOrganizationOptions(search),
1000
);
},
async fetchOrganizationOptions(search: string) {
- this.loadingOrganizations = true;
await this.userStore.retrieveOrganizations(search);
- this.loadingOrganizations = false;
+ this.isLoadingOrganizations = false;
},
},
});
diff --git a/src/modules/user/pages/modals/MergeUserModal.vue b/src/modules/user/pages/modals/MergeUserModal.vue
index f081af403226ab5584f18148402dbf45b5af32e7..533cf4b60cb5edc24766929c0e3d6c704057895c 100644
--- a/src/modules/user/pages/modals/MergeUserModal.vue
+++ b/src/modules/user/pages/modals/MergeUserModal.vue
@@ -59,13 +59,13 @@ import { defineComponent, type PropType } from "vue";
import useLoginStore from "@/modules/login/store";
import useUserStore from "../../store";
-import { ExternalAuthenticators } from "../../types";
+import type { IdentityProviders } from "@coscine/api-client/dist/types/Coscine.Api";
export default defineComponent({
props: {
providerToMerge: {
default: null,
- type: String as PropType<ExternalAuthenticators | null>,
+ type: String as PropType<IdentityProviders | null>,
},
},
setup() {
diff --git a/src/modules/user/store.ts b/src/modules/user/store.ts
index 68ff508c72826269ea9817b43f63cb15658479a3..b58305ff0c4205057b2e7c4a4910f6ee618857f5 100644
--- a/src/modules/user/store.ts
+++ b/src/modules/user/store.ts
@@ -1,8 +1,9 @@
import {
LanguageApi,
OrganizationApi,
+ SelfApi,
+ SelfApiTokenApi,
TitleApi,
- TokenApi,
UserApi,
} from "@coscine/api-client";
import { defineStore } from "pinia";
@@ -18,7 +19,7 @@ import { removeQueryParameterFromUrl } from "@/router";
import { DisciplineApi } from "@coscine/api-client";
import type {
ApiTokenForCreationDto,
- ExternalAuthenticators,
+ IdentityProviders,
UserForUpdateDto,
UserMergeDto,
} from "@coscine/api-client/dist/types/Coscine.Api";
@@ -98,7 +99,7 @@ export const useUserStore = defineStore({
try {
const confirmationToken = route.query[tokenKey]?.toString();
if (confirmationToken) {
- await UserApi.confirmUserEmail(confirmationToken);
+ await SelfApi.confirmUserEmail(confirmationToken);
notificationStore.postNotification({
title: i18n.t("toast.contactChange.success.title").toString(),
body: i18n.t("toast.contactChange.success.message").toString(),
@@ -171,7 +172,7 @@ export const useUserStore = defineStore({
async retrieveTokens() {
const notificationStore = useNotificationStore();
try {
- let apiResponse = await TokenApi.getAllApiTokens();
+ let apiResponse = await SelfApiTokenApi.getAllApiTokens();
if (apiResponse.data.data) {
// Assign the data from the first page
this.userProfile.tokens = [...apiResponse.data.data];
@@ -186,7 +187,7 @@ export const useUserStore = defineStore({
? apiResponse.data.pagination.currentPage + 1
: 1;
// Retrieve the next page
- apiResponse = await TokenApi.getAllApiTokens(nextPage);
+ apiResponse = await SelfApiTokenApi.getAllApiTokens(nextPage);
if (apiResponse.data.data) {
// Extend the data with the newly fetched data
this.userProfile.tokens = [...apiResponse.data.data];
@@ -204,7 +205,7 @@ export const useUserStore = defineStore({
async retrieveUser() {
const notificationStore = useNotificationStore();
try {
- const apiResponse = await UserApi.getCurrentUser();
+ const apiResponse = await SelfApi.getCurrentUser();
this.user = apiResponse.data.data;
} catch (error) {
// Handle other Status Codes
@@ -215,7 +216,7 @@ export const useUserStore = defineStore({
async deleteToken(tokenId: string) {
const notificationStore = useNotificationStore();
try {
- await TokenApi.revokeToken(tokenId);
+ await SelfApiTokenApi.revokeToken(tokenId);
} catch (error) {
// Handle other Status Codes
notificationStore.postApiErrorNotification(error as AxiosError);
@@ -223,11 +224,11 @@ export const useUserStore = defineStore({
},
async initiateUserMerge(
- provider: ExternalAuthenticators
+ provider: IdentityProviders
): Promise<UserMergeDto | null | undefined> {
const notificationStore = useNotificationStore();
try {
- const apiResponse = await UserApi.initiateUserMerge(provider);
+ const apiResponse = await SelfApi.initiateUserMerge(provider);
return apiResponse.data.data;
} catch (error) {
// Handle other Status Codes
@@ -239,7 +240,9 @@ export const useUserStore = defineStore({
async createApiToken(tokenForCreation: ApiTokenForCreationDto) {
const notificationStore = useNotificationStore();
try {
- const apiResponse = await TokenApi.createApiToken(tokenForCreation);
+ const apiResponse = await SelfApiTokenApi.createApiToken(
+ tokenForCreation
+ );
return apiResponse.data.data;
} catch (error) {
// Handle other Status Codes
@@ -250,7 +253,7 @@ export const useUserStore = defineStore({
async updateUser(userForUpdateDto: UserForUpdateDto): Promise<boolean> {
const notificationStore = useNotificationStore();
try {
- await UserApi.updateCurrentUser(userForUpdateDto);
+ await SelfApi.updateCurrentUser(userForUpdateDto);
return true;
} catch (error) {
// Handle other Status Codes
diff --git a/src/modules/user/types.ts b/src/modules/user/types.ts
index 86888fd0931b7c78bc20ed483d5181e6348599a2..24092a0c008cf3e7bc3ddccc5c13a09801e37bbd 100644
--- a/src/modules/user/types.ts
+++ b/src/modules/user/types.ts
@@ -29,9 +29,9 @@ export interface TokenValidityBoundDates {
}
/**
- * Defining the {@link ExternalAuthenticators} enum inside the types, because it is not being exported correctly from the @coscine/api-client library.
+ * Defining the {@link IdentityProviders} enum inside the types, because it is not being exported correctly from the @coscine/api-client library.
*/
-export enum ExternalAuthenticators {
+export enum IdentityProviders {
Shibboleth = "Shibboleth",
OrciD = "ORCiD",
}
diff --git a/src/plugins/mockupPinia.ts b/src/plugins/mockupPinia.ts
index 7b00a241478aae41962de4358104a44dc59f8aca..5669bb52de256634fae47a0bba1493c72833b596 100644
--- a/src/plugins/mockupPinia.ts
+++ b/src/plugins/mockupPinia.ts
@@ -1,6 +1,6 @@
import { createTestingPinia } from "@pinia/testing";
import { testProjectState } from "@/data/mockup/testProject";
-import { getTestUserState } from "@/data/mockup/testUser";
+import { getTestShibbolethUserState } from "@/data/mockup/testUser";
import { getTestResourceState } from "@/data/mockup/testResource";
import { testLoginState } from "@/data/mockup/testLogin";
import { getTestMainState } from "@/data/mockup/testMain";
@@ -14,7 +14,7 @@ const pinia = createTestingPinia({
project: testProjectState,
resource: await getTestResourceState(),
search: testSearchState,
- user: getTestUserState(),
+ user: getTestShibbolethUserState(),
},
});
diff --git a/src/store/index.ts b/src/store/index.ts
index 4df0ff57184a8d89ddd13045da0a51d0e1e7e176..ef6032244a8265b8cf3305b84150c28a2f92d2f0 100644
--- a/src/store/index.ts
+++ b/src/store/index.ts
@@ -105,8 +105,9 @@ export const useMainStore = defineStore({
},
async getMaintenance() {
- const apiResponse = await MaintenanceApi.getCurrentMaintenance();
- const maintenance = apiResponse.data.data;
+ const apiResponse = await MaintenanceApi.getCurrentMaintenances();
+ // TODO: Make it work with multiple maintenance messages
+ const maintenance = apiResponse.data.data?.at(0); // Take the first maintenance
if (maintenance?.startsDate) {
const now = new Date(Date.now());
const startDate = new Date(maintenance.startsDate);
diff --git a/src/store/notification.ts b/src/store/notification.ts
index cca0fe9ee6c3fdb110f1bc42ed42d7590d44180b..4f215f532cade98efc1d1f0a3e97dc6bb2e0c30b 100644
--- a/src/store/notification.ts
+++ b/src/store/notification.ts
@@ -83,7 +83,7 @@ export const useNotificationStore = defineStore({
},
postNotification(toast: NotificationToast) {
- this.notificationQueue.push(toast);
+ this.notificationQueue.push(toast); // TODO: Address this warning
},
deleteNotification(toast: NotificationToast) {
diff --git a/src/store/types.ts b/src/store/types.ts
index 2a3751dd667c4880babcb3d0308f596b8bd4b832..33c8674478883f9a39405167130e53e8456da921 100644
--- a/src/store/types.ts
+++ b/src/store/types.ts
@@ -2,10 +2,9 @@ import type { RemovableRef } from "@vueuse/core";
import type { MaintenanceDto } from "@coscine/api-client/dist/types/Coscine.Api";
import type { VNode } from "vue";
import type { BvToastOptions } from "bootstrap-vue";
-import type { TranslateResult } from "vue-i18n";
export interface NotificationToast extends BvToastOptions {
- body: string | TranslateResult | VNode | Array<VNode>;
+ body: string | VNode | Array<VNode>;
variant?: "danger" | "warning" | "success" | "info" | "primary" | "secondary";
}