From e0c501cd179bb9ac45f07b8ea36b689ac1790087 Mon Sep 17 00:00:00 2001 From: sgiannopoulos Date: Mon, 20 May 2024 12:20:21 +0300 Subject: [PATCH 1/3] repository login changes --- .../org/opencdmp/audit/AuditableAction.java | 1 + .../notification/NotificationProperties.java | 28 ++--- .../model/persist/deposit/DepositRequest.java | 16 +-- .../controllers/DepositController.java | 40 +++++-- .../main/resources/config/deposit-devel.yml | 2 +- dmp-frontend/src/app/app-routing.module.ts | 7 +- dmp-frontend/src/app/app.module.ts | 4 +- .../core/services/deposit/deposit.service.ts | 13 ++- .../src/app/ui/auth/login/login.module.ts | 4 +- .../dmp-deposit-dropdown.component.ts | 53 ++++----- .../deposit-oauth2-dialog-routing.module.ts | 27 +++++ .../deposit-oauth2-dialog.component.html} | 0 .../deposit-oauth2-dialog.component.scss} | 0 .../deposit-oauth2-dialog.component.ts | 103 ++++++++++++++++++ .../deposit-oauth2-dialog.module.ts | 19 ++++ .../service/deposit-oauth2-dialog.service.ts} | 31 ++---- .../oauth2-dialog/oauth2-dialog.component.ts | 48 -------- .../oauth2-dialog/oauth2-dialog.module.ts | 17 --- 18 files changed, 255 insertions(+), 158 deletions(-) create mode 100644 dmp-frontend/src/app/ui/misc/deposit-oauth2-dialog/deposit-oauth2-dialog-routing.module.ts rename dmp-frontend/src/app/ui/misc/{oauth2-dialog/oauth2-dialog.component.html => deposit-oauth2-dialog/deposit-oauth2-dialog.component.html} (100%) rename dmp-frontend/src/app/ui/misc/{oauth2-dialog/oauth2-dialog.component.scss => deposit-oauth2-dialog/deposit-oauth2-dialog.component.scss} (100%) create mode 100644 dmp-frontend/src/app/ui/misc/deposit-oauth2-dialog/deposit-oauth2-dialog.component.ts create mode 100644 dmp-frontend/src/app/ui/misc/deposit-oauth2-dialog/deposit-oauth2-dialog.module.ts rename dmp-frontend/src/app/ui/misc/{oauth2-dialog/service/oauth2-dialog.service.ts => deposit-oauth2-dialog/service/deposit-oauth2-dialog.service.ts} (55%) delete mode 100644 dmp-frontend/src/app/ui/misc/oauth2-dialog/oauth2-dialog.component.ts delete mode 100644 dmp-frontend/src/app/ui/misc/oauth2-dialog/oauth2-dialog.module.ts diff --git a/backend/core/src/main/java/org/opencdmp/audit/AuditableAction.java b/backend/core/src/main/java/org/opencdmp/audit/AuditableAction.java index 085dedac1..c0c84243f 100644 --- a/backend/core/src/main/java/org/opencdmp/audit/AuditableAction.java +++ b/backend/core/src/main/java/org/opencdmp/audit/AuditableAction.java @@ -137,6 +137,7 @@ public class AuditableAction { public static final EventId Deposit_GetAccessToken = new EventId(18001, "Deposit_GetAccessToken"); public static final EventId Deposit_Deposit = new EventId(18002, "Deposit_Deposit"); public static final EventId Deposit_GetLogo = new EventId(18003, "Deposit_GetLogo"); + public static final EventId Deposit_GetRepository = new EventId(18004, "Deposit_GetRepository"); public static final EventId Tag_Query = new EventId(19000, "Tag_Query"); public static final EventId Tag_Lookup = new EventId(19001, "Tag_Lookup"); diff --git a/backend/core/src/main/java/org/opencdmp/commons/notification/NotificationProperties.java b/backend/core/src/main/java/org/opencdmp/commons/notification/NotificationProperties.java index 4da201dcf..f434964f5 100644 --- a/backend/core/src/main/java/org/opencdmp/commons/notification/NotificationProperties.java +++ b/backend/core/src/main/java/org/opencdmp/commons/notification/NotificationProperties.java @@ -23,7 +23,7 @@ public class NotificationProperties { private String contactSupportEmail; public UUID getDmpInvitationExternalUserType() { - return dmpInvitationExternalUserType; + return this.dmpInvitationExternalUserType; } public void setDmpInvitationExternalUserType(UUID dmpInvitationExternalUserType) { @@ -31,7 +31,7 @@ public class NotificationProperties { } public UUID getDmpInvitationExistingUserType() { - return dmpInvitationExistingUserType; + return this.dmpInvitationExistingUserType; } public void setDmpInvitationExistingUserType(UUID dmpInvitationExistingUserType) { @@ -39,7 +39,7 @@ public class NotificationProperties { } public UUID getDmpModifiedType() { - return dmpModifiedType; + return this.dmpModifiedType; } public void setDmpModifiedType(UUID dmpModifiedType) { @@ -47,7 +47,7 @@ public class NotificationProperties { } public UUID getDmpFinalisedType() { - return dmpFinalisedType; + return this.dmpFinalisedType; } public void setDmpFinalisedType(UUID dmpFinalisedType) { @@ -55,7 +55,7 @@ public class NotificationProperties { } public UUID getDescriptionModifiedType() { - return descriptionModifiedType; + return this.descriptionModifiedType; } public void setDescriptionModifiedType(UUID descriptionModifiedType) { @@ -63,7 +63,7 @@ public class NotificationProperties { } public UUID getDescriptionFinalisedType() { - return descriptionFinalisedType; + return this.descriptionFinalisedType; } public void setDescriptionFinalisedType(UUID descriptionFinalisedType) { @@ -71,7 +71,7 @@ public class NotificationProperties { } public UUID getMergeAccountConfirmationType() { - return mergeAccountConfirmationType; + return this.mergeAccountConfirmationType; } public void setMergeAccountConfirmationType(UUID mergeAccountConfirmationType) { @@ -79,7 +79,7 @@ public class NotificationProperties { } public UUID getRemoveCredentialConfirmationType() { - return removeCredentialConfirmationType; + return this.removeCredentialConfirmationType; } public void setRemoveCredentialConfirmationType(UUID removeCredentialConfirmationType) { @@ -87,7 +87,7 @@ public class NotificationProperties { } public UUID getDmpDepositType() { - return dmpDepositType; + return this.dmpDepositType; } public void setDmpDepositType(UUID dmpDepositType) { @@ -95,7 +95,7 @@ public class NotificationProperties { } public UUID getDescriptionTemplateInvitationType() { - return descriptionTemplateInvitationType; + return this.descriptionTemplateInvitationType; } public void setDescriptionTemplateInvitationType(UUID descriptionTemplateInvitationType) { @@ -103,7 +103,7 @@ public class NotificationProperties { } public int getEmailExpirationTimeSeconds() { - return emailExpirationTimeSeconds; + return this.emailExpirationTimeSeconds; } public void setEmailExpirationTimeSeconds(int emailExpirationTimeSeconds) { @@ -111,7 +111,7 @@ public class NotificationProperties { } public UUID getContactSupportType() { - return contactSupportType; + return this.contactSupportType; } public void setContactSupportType(UUID contactSupportType) { @@ -119,7 +119,7 @@ public class NotificationProperties { } public UUID getPublicContactSupportType() { - return publicContactSupportType; + return this.publicContactSupportType; } public void setPublicContactSupportType(UUID publicContactSupportType) { @@ -127,7 +127,7 @@ public class NotificationProperties { } public String getContactSupportEmail() { - return contactSupportEmail; + return this.contactSupportEmail; } public void setContactSupportEmail(String contactSupportEmail) { diff --git a/backend/core/src/main/java/org/opencdmp/model/persist/deposit/DepositRequest.java b/backend/core/src/main/java/org/opencdmp/model/persist/deposit/DepositRequest.java index b58cc2977..af25da4b2 100644 --- a/backend/core/src/main/java/org/opencdmp/model/persist/deposit/DepositRequest.java +++ b/backend/core/src/main/java/org/opencdmp/model/persist/deposit/DepositRequest.java @@ -1,10 +1,10 @@ package org.opencdmp.model.persist.deposit; -import org.opencdmp.commons.validation.BaseValidator; +import gr.cite.tools.fieldset.BaseFieldSet; import gr.cite.tools.validation.specification.Specification; +import org.opencdmp.commons.validation.BaseValidator; import org.opencdmp.convention.ConventionService; import org.opencdmp.errorcode.ErrorThesaurusProperties; -import gr.cite.tools.fieldset.BaseFieldSet; import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.context.MessageSource; import org.springframework.context.annotation.Scope; @@ -30,7 +30,7 @@ public class DepositRequest { private BaseFieldSet project; public String getRepositoryId() { - return repositoryId; + return this.repositoryId; } public void setRepositoryId(String repositoryId) { @@ -38,7 +38,7 @@ public class DepositRequest { } public UUID getDmpId() { - return dmpId; + return this.dmpId; } public void setDmpId(UUID dmpId) { @@ -46,7 +46,7 @@ public class DepositRequest { } public String getAccessToken() { - return accessToken; + return this.accessToken; } public void setAccessToken(String accessToken) { @@ -54,7 +54,7 @@ public class DepositRequest { } public BaseFieldSet getProject() { - return project; + return this.project; } public void setProject(BaseFieldSet project) { @@ -84,10 +84,10 @@ public class DepositRequest { return Arrays.asList( this.spec() .must(() -> !this.isEmpty(item.getRepositoryId())) - .failOn(DepositRequest._repositoryId).failWith(messageSource.getMessage("Validation_Required", new Object[]{DepositRequest._repositoryId}, LocaleContextHolder.getLocale())), + .failOn(DepositRequest._repositoryId).failWith(this.messageSource.getMessage("Validation_Required", new Object[]{DepositRequest._repositoryId}, LocaleContextHolder.getLocale())), this.spec() .must(() -> this.isValidGuid(item.getDmpId())) - .failOn(DepositRequest._dmpId).failWith(messageSource.getMessage("Validation_Required", new Object[]{DepositRequest._dmpId}, LocaleContextHolder.getLocale())) + .failOn(DepositRequest._dmpId).failWith(this.messageSource.getMessage("Validation_Required", new Object[]{DepositRequest._dmpId}, LocaleContextHolder.getLocale())) ); } } diff --git a/backend/web/src/main/java/org/opencdmp/controllers/DepositController.java b/backend/web/src/main/java/org/opencdmp/controllers/DepositController.java index 6652df4f9..93c1c1394 100644 --- a/backend/web/src/main/java/org/opencdmp/controllers/DepositController.java +++ b/backend/web/src/main/java/org/opencdmp/controllers/DepositController.java @@ -1,7 +1,13 @@ package org.opencdmp.controllers; -import org.opencdmp.audit.AuditableAction; +import gr.cite.tools.auditing.AuditService; +import gr.cite.tools.data.censor.CensorFactory; +import gr.cite.tools.exception.MyNotFoundException; +import gr.cite.tools.fieldset.FieldSet; +import gr.cite.tools.logging.LoggerService; +import gr.cite.tools.logging.MapLogEntry; import gr.cite.tools.validation.ValidationFilterAnnotation; +import org.opencdmp.audit.AuditableAction; import org.opencdmp.model.EntityDoi; import org.opencdmp.model.censorship.EntityDoiCensor; import org.opencdmp.model.censorship.deposit.DepositConfigurationCensor; @@ -9,13 +15,10 @@ import org.opencdmp.model.deposit.DepositConfiguration; import org.opencdmp.model.persist.deposit.DepositAuthenticateRequest; import org.opencdmp.model.persist.deposit.DepositRequest; import org.opencdmp.service.deposit.DepositService; -import gr.cite.tools.auditing.AuditService; -import gr.cite.tools.data.censor.CensorFactory; -import gr.cite.tools.fieldset.FieldSet; -import gr.cite.tools.logging.LoggerService; -import gr.cite.tools.logging.MapLogEntry; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.MessageSource; +import org.springframework.context.i18n.LocaleContextHolder; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.bind.annotation.*; @@ -32,7 +35,7 @@ import java.util.Map; @RestController @CrossOrigin -@RequestMapping(value = {"/api/deposit/"}) +@RequestMapping("/api/deposit/") public class DepositController { private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(DepositController.class)); @@ -44,11 +47,14 @@ public class DepositController { private final AuditService auditService; + private final MessageSource messageSource; + @Autowired - public DepositController(DepositService depositService, CensorFactory censorFactory, AuditService auditService) { + public DepositController(DepositService depositService, CensorFactory censorFactory, AuditService auditService, MessageSource messageSource) { this.depositService = depositService; this.censorFactory = censorFactory; this.auditService = auditService; + this.messageSource = messageSource; } @GetMapping("/repositories/available") @@ -93,6 +99,24 @@ public class DepositController { return persisted; } + @GetMapping("/repositories/{repositoryId}") + public org.opencdmp.model.deposit.DepositConfiguration getRepository(@PathVariable("repositoryId") String repositoryId, FieldSet fieldSet) throws InvalidApplicationException, InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException { + logger.debug(new MapLogEntry("retrieving" + DepositConfiguration.class.getSimpleName()).And("fields", fieldSet).And("repositoryId", repositoryId)); + + this.censorFactory.censor(DepositConfigurationCensor.class).censor(fieldSet, null); + + List models = this.depositService.getAvailableConfigurations(fieldSet); + org.opencdmp.model.deposit.DepositConfiguration model = models.stream().filter(x-> x.getRepositoryId().equals(repositoryId)).findFirst().orElse(null); + if (model == null) throw new MyNotFoundException(this.messageSource.getMessage("General_ItemNotFound", new Object[]{repositoryId, org.opencdmp.model.deposit.DepositConfiguration.class.getSimpleName()}, LocaleContextHolder.getLocale())); + + this.auditService.track(AuditableAction.Deposit_GetRepository, Map.ofEntries( + new AbstractMap.SimpleEntry("repositoryId", repositoryId), + new AbstractMap.SimpleEntry("fields", fieldSet) + )); + + return model; + } + @GetMapping("/repositories/{repositoryId}/logo") public String getLogo(@PathVariable("repositoryId") String repositoryId) throws InvalidApplicationException, InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException { logger.debug(new MapLogEntry("get logo" + DepositConfiguration.class.getSimpleName()).And("repositoryId", repositoryId)); diff --git a/backend/web/src/main/resources/config/deposit-devel.yml b/backend/web/src/main/resources/config/deposit-devel.yml index 6eb01fe91..5266b1b75 100644 --- a/backend/web/src/main/resources/config/deposit-devel.yml +++ b/backend/web/src/main/resources/config/deposit-devel.yml @@ -1,7 +1,7 @@ deposit: sources: - url: http://dev04.local.cite.gr:55330/zenodo - repositoryId: Zenodo + repositoryId: zenodo pdfTransformerId: docx-file-transformer rdaTransformerId: rda-file-transformer issuer-url: ${IDP_ISSUER_URI_TOKEN} diff --git a/dmp-frontend/src/app/app-routing.module.ts b/dmp-frontend/src/app/app-routing.module.ts index 41a93bf18..f83680b2e 100644 --- a/dmp-frontend/src/app/app-routing.module.ts +++ b/dmp-frontend/src/app/app-routing.module.ts @@ -1,10 +1,9 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; -import { Oauth2DialogComponent } from './ui/misc/oauth2-dialog/oauth2-dialog.component'; -import { AppComponent } from './app.component'; import { AppPermission } from './core/common/enum/permission.enum'; import { BreadcrumbService } from './ui/misc/breadcrumb/breadcrumb.service'; import { ReloadHelperComponent } from './ui/misc/reload-helper/reload-helper.component'; +import { DepositOauth2DialogComponent } from './ui/misc/deposit-oauth2-dialog/deposit-oauth2-dialog.component'; const appRoutes: Routes = [ { @@ -367,7 +366,9 @@ const appRoutes: Routes = [ }, { path: 'logout', loadChildren: () => import('./ui/auth/logout/logout.module').then(m => m.LogoutModule) }, { path: 'reload', component: ReloadHelperComponent }, - { path: 'oauth2', component: Oauth2DialogComponent }, + { path: 'oauth2', component: DepositOauth2DialogComponent }, + { path: 'login/external/zenodo', component: DepositOauth2DialogComponent }, + { path: 'deposit/oauth2', loadChildren: () => import('./ui/misc/deposit-oauth2-dialog/deposit-oauth2-dialog.module').then(m => m.DepositOauth2DialogModule) } ]; @NgModule({ diff --git a/dmp-frontend/src/app/app.module.ts b/dmp-frontend/src/app/app.module.ts index d7916c359..e61742887 100644 --- a/dmp-frontend/src/app/app.module.ts +++ b/dmp-frontend/src/app/app.module.ts @@ -36,10 +36,10 @@ import { LanguageService } from './core/services/language/language.service'; import { TranslateServerLoader } from './core/services/language/server.loader'; import { MatomoService } from './core/services/matomo/matomo-service'; import { GuidedTourModule } from './library/guided-tour/guided-tour.module'; -import { Oauth2DialogModule } from './ui/misc/oauth2-dialog/oauth2-dialog.module'; import { OpenDMPCustomTranslationCompiler } from './utilities/translate/opendmp-custom-translation-compiler'; import { CoreAnnotationServiceModule } from 'annotation-service/services/core-service.module'; import { CoreNotificationServiceModule } from '@notification-service/services/core-service.module'; +import { DepositOauth2DialogModule } from './ui/misc/deposit-oauth2-dialog/deposit-oauth2-dialog.module'; // AoT requires an exported function for factories export function HttpLoaderFactory(languageHttpService: LanguageHttpService) { @@ -145,7 +145,7 @@ export function InstallationConfigurationFactory(appConfig: ConfigurationService NavbarModule, SidebarModule, NgcCookieConsentModule.forRoot(cookieConfig), - Oauth2DialogModule, + DepositOauth2DialogModule, GuidedTourModule.forRoot(), DragulaModule.forRoot(), NgxMatomoModule.forRoot({ diff --git a/dmp-frontend/src/app/core/services/deposit/deposit.service.ts b/dmp-frontend/src/app/core/services/deposit/deposit.service.ts index 4c8cf1803..2946397b0 100644 --- a/dmp-frontend/src/app/core/services/deposit/deposit.service.ts +++ b/dmp-frontend/src/app/core/services/deposit/deposit.service.ts @@ -30,9 +30,18 @@ export class DepositService { catchError((error: any) => throwError(error))); } - getAccessToken(item: DepositAuthenticateRequest): Observable { + getRepository(repositoryId: string, reqFields: string[] = []): Observable { + const url = `${this.apiBase}//repositories/${repositoryId}`; + const options = { params: { f: reqFields } }; + + return this.http + .get(url, options).pipe( + catchError((error: any) => throwError(error))); + } + + getAccessToken(item: DepositAuthenticateRequest): Observable { const url = `${this.apiBase}/get-access-token`; - return this.http.post(url, item).pipe(catchError((error: any) => throwError(error))); + return this.http.post(url, item).pipe(catchError((error: any) => throwError(error))); } deposit(item: DepositRequest): Observable { diff --git a/dmp-frontend/src/app/ui/auth/login/login.module.ts b/dmp-frontend/src/app/ui/auth/login/login.module.ts index 4cafdc86e..6546559d7 100644 --- a/dmp-frontend/src/app/ui/auth/login/login.module.ts +++ b/dmp-frontend/src/app/ui/auth/login/login.module.ts @@ -2,7 +2,6 @@ import { NgModule } from '@angular/core'; import { LoginComponent } from '@app/ui/auth/login/login.component'; import { LoginRoutingModule } from '@app/ui/auth/login/login.routing'; import { LoginService } from '@app/ui/auth/login/utilities/login.service'; -import { Oauth2DialogModule } from '@app/ui/misc/oauth2-dialog/oauth2-dialog.module'; import { CommonFormsModule } from '@common/forms/common-forms.module'; import { CommonUiModule } from '@common/ui/common-ui.module'; import { MergeEmailConfirmation } from './merge-email-confirmation/merge-email-confirmation.component'; @@ -13,8 +12,7 @@ import { PostLoginComponent } from './post-login/post-login.component'; imports: [ CommonUiModule, CommonFormsModule, - LoginRoutingModule, - Oauth2DialogModule + LoginRoutingModule ], declarations: [ LoginComponent, diff --git a/dmp-frontend/src/app/ui/dmp/editor/dmp-deposit-dropdown/dmp-deposit-dropdown.component.ts b/dmp-frontend/src/app/ui/dmp/editor/dmp-deposit-dropdown/dmp-deposit-dropdown.component.ts index 0d3647d2e..53192679a 100644 --- a/dmp-frontend/src/app/ui/dmp/editor/dmp-deposit-dropdown/dmp-deposit-dropdown.component.ts +++ b/dmp-frontend/src/app/ui/dmp/editor/dmp-deposit-dropdown/dmp-deposit-dropdown.component.ts @@ -11,7 +11,7 @@ import { SnackBarNotificationLevel, UiNotificationService } from '@app/core/services/notification/ui-notification-service'; -import { Oauth2DialogService } from '@app/ui/misc/oauth2-dialog/service/oauth2-dialog.service'; +import { DepositOauth2DialogService } from '@app/ui/misc/deposit-oauth2-dialog/service/deposit-oauth2-dialog.service'; import { BaseComponent } from '@common/base/base.component'; import { MultipleChoiceDialogComponent } from '@common/modules/multiple-choice-dialog/multiple-choice-dialog.component'; import { TranslateService } from '@ngx-translate/core'; @@ -37,7 +37,7 @@ export class DmpDepositDropdown extends BaseComponent implements OnInit { private language: TranslateService, private translate: TranslateService, private uiNotificationService: UiNotificationService, - private oauth2DialogService: Oauth2DialogService, + private depositOauth2DialogService: DepositOauth2DialogService, private sanitizer: DomSanitizer ) { super(); @@ -75,9 +75,7 @@ export class DmpDepositDropdown extends BaseComponent implements OnInit { dialogRef.afterClosed().pipe(takeUntil(this._destroyed)).subscribe(result => { switch (result) { case 0: - this.showOauth2Dialog(repo.repositoryAuthorizationUrl + '?client_id=' + repo.repositoryClientId - + '&response_type=code&scope=deposit:write+deposit:actions+user:email&state=astate&redirect_uri=' - + repo.redirectUri, repo, this.dmp); + this.showOauth2Dialog(this.depositOauth2DialogService.getLoginUrl(repo, this.dmp?.id), repo, this.dmp); break; case 1: const depositRequest: DepositRequest = { @@ -123,34 +121,25 @@ export class DmpDepositDropdown extends BaseComponent implements OnInit { } showOauth2Dialog(url: string, repo: DepositConfiguration, dmp: Dmp) { - this.oauth2DialogService.login(url) + this.depositOauth2DialogService.login(url) .pipe(takeUntil(this._destroyed)) - .subscribe(result => { - if (result !== undefined) { - if (result.oauthCode !== undefined && result.oauthCode !== null && !this.oauthLock) { - const depositAuthenticateRequest: DepositAuthenticateRequest = { - repositoryId: repo.repositoryId, - code: result.oauthCode - }; - this.depositRepositoriesService.getAccessToken(depositAuthenticateRequest) - .pipe(takeUntil(this._destroyed)) - .subscribe(token => { - const depositRequest: DepositRequest = { - repositoryId: repo.repositoryId, - dmpId: dmp.id, - accessToken: token, - project: this.EntityDoiFields() - }; - this.depositRepositoriesService.deposit(depositRequest) - .pipe(takeUntil(this._destroyed)) - .subscribe(doi => { - this.onDOICallbackSuccess(); - this.outputRepos.push(doi); - this.outputReposEmitter.emit(this.outputRepos); - }, error => this.onDOICallbackError(error)); - }); - this.oauthLock = true; - } + .subscribe(token => { + if (token !== undefined) { + console.log(token); + // const depositRequest: DepositRequest = { + // repositoryId: repo.repositoryId, + // dmpId: dmp.id, + // accessToken: token, + // project: this.EntityDoiFields() + // }; + // this.depositRepositoriesService.deposit(depositRequest) + // .pipe(takeUntil(this._destroyed)) + // .subscribe(doi => { + // this.onDOICallbackSuccess(); + // this.outputRepos.push(doi); + // this.outputReposEmitter.emit(this.outputRepos); + // }, error => this.onDOICallbackError(error)); + this.oauthLock = true; } else { this.oauthLock = false; } diff --git a/dmp-frontend/src/app/ui/misc/deposit-oauth2-dialog/deposit-oauth2-dialog-routing.module.ts b/dmp-frontend/src/app/ui/misc/deposit-oauth2-dialog/deposit-oauth2-dialog-routing.module.ts new file mode 100644 index 000000000..8c5350e64 --- /dev/null +++ b/dmp-frontend/src/app/ui/misc/deposit-oauth2-dialog/deposit-oauth2-dialog-routing.module.ts @@ -0,0 +1,27 @@ +import { NgModule } from '@angular/core'; +import { RouterModule, Routes } from '@angular/router'; +import { DepositOauth2DialogComponent } from './deposit-oauth2-dialog.component'; + +const routes: Routes = [ + { + path: ':id/:entityId', + component: DepositOauth2DialogComponent, + data: { + breadcrumb: true + }, + }, + { + path: 'code-callaback', + component: DepositOauth2DialogComponent, + data: { + breadcrumb: true + }, + }, +]; + +@NgModule({ + imports: [RouterModule.forChild(routes)], + exports: [RouterModule], + providers: [] +}) +export class DepositOauth2DialogRoutingModule { } diff --git a/dmp-frontend/src/app/ui/misc/oauth2-dialog/oauth2-dialog.component.html b/dmp-frontend/src/app/ui/misc/deposit-oauth2-dialog/deposit-oauth2-dialog.component.html similarity index 100% rename from dmp-frontend/src/app/ui/misc/oauth2-dialog/oauth2-dialog.component.html rename to dmp-frontend/src/app/ui/misc/deposit-oauth2-dialog/deposit-oauth2-dialog.component.html diff --git a/dmp-frontend/src/app/ui/misc/oauth2-dialog/oauth2-dialog.component.scss b/dmp-frontend/src/app/ui/misc/deposit-oauth2-dialog/deposit-oauth2-dialog.component.scss similarity index 100% rename from dmp-frontend/src/app/ui/misc/oauth2-dialog/oauth2-dialog.component.scss rename to dmp-frontend/src/app/ui/misc/deposit-oauth2-dialog/deposit-oauth2-dialog.component.scss diff --git a/dmp-frontend/src/app/ui/misc/deposit-oauth2-dialog/deposit-oauth2-dialog.component.ts b/dmp-frontend/src/app/ui/misc/deposit-oauth2-dialog/deposit-oauth2-dialog.component.ts new file mode 100644 index 000000000..60ba4f18e --- /dev/null +++ b/dmp-frontend/src/app/ui/misc/deposit-oauth2-dialog/deposit-oauth2-dialog.component.ts @@ -0,0 +1,103 @@ +import { Component, OnInit, Inject } from '@angular/core'; +import { ActivatedRoute, ParamMap, Params } from '@angular/router'; +import { BaseComponent } from '@common/base/base.component'; +import { takeUntil } from 'rxjs/operators'; +import { DepositOauth2DialogService } from './service/deposit-oauth2-dialog.service'; +import { DepositService } from '@app/core/services/deposit/deposit.service'; +import { DepositConfiguration } from '@app/core/model/deposit/deposit-configuration'; +import { nameof } from 'ts-simple-nameof'; +import { HttpErrorResponse } from '@angular/common/http'; +import { HttpError, HttpErrorHandlingService } from '@common/modules/errors/error-handling/http-error-handling.service'; +import { SnackBarNotificationLevel, UiNotificationService } from '@app/core/services/notification/ui-notification-service'; +import { DepositAuthenticateRequest, DepositRequest, DepositRequestFields } from '@app/core/model/deposit/deposit-request'; +import { EntityDoi } from '@app/core/model/entity-doi/entity-doi'; + +@Component({ + selector: 'app-deposit-oauth2-dialog', + templateUrl: './deposit-oauth2-dialog.component.html', + styleUrls: ['./deposit-oauth2-dialog.component.scss'] +}) +export class DepositOauth2DialogComponent extends BaseComponent implements OnInit{ + + constructor( + private route: ActivatedRoute, + private depositRepositoriesService: DepositService, + private uiNotificationService: UiNotificationService, + private httpErrorHandlingService: HttpErrorHandlingService, + private depositOauth2DialogService: DepositOauth2DialogService + ) { + super(); + } + + error: string = null; + + ngOnInit(): void { + this.route.paramMap.pipe(takeUntil(this._destroyed)).subscribe((paramMap: ParamMap) => { + let itemId = paramMap.get('id'); + let entityId = paramMap.get('entityId'); + + if (itemId && entityId) { + this.depositRepositoriesService.getRepository(itemId, [ + nameof(x => x.depositType), + nameof(x => x.repositoryId), + nameof(x => x.repositoryAuthorizationUrl), + nameof(x => x.repositoryRecordUrl), + nameof(x => x.repositoryClientId), + nameof(x => x.hasLogo), + nameof(x => x.redirectUri) + ]) + .pipe(takeUntil(this._destroyed)) + .subscribe( + repo => { + this.loadUrl(this.depositOauth2DialogService.getLoginUrl(repo, entityId)); + }, + error => this.onCallbackError(error)); + } else { + this.error = 'Repository id required' + } + }); + this.route.queryParams.pipe(takeUntil(this._destroyed)) + .subscribe((params: Params) => { + console.log(params) + if (params['url']) { + this.loadUrl(params['url']) + } else if (params['code']) { + if (!params['state']) { + this.error = 'State required' + + } else { + this.getAccessToken(params['code'], params['state']); + } + } else { + this.error = 'Repository id required' + } + }); + } + + private loadUrl(url: string ) { + window.location.href = url; + } + + private getAccessToken(code: string, state: string) { + const decoded = decodeURIComponent(state); + const stateParsed = JSON.parse(atob(decoded)); + if (!stateParsed) this.error = 'State required'; + if (!stateParsed.repositoryId) this.error = 'State repository id required'; + if (!stateParsed.entity) this.error = 'State entity required'; + + const depositAuthenticateRequest: DepositAuthenticateRequest = { + repositoryId: stateParsed.repositoryId, + code: code + }; + this.depositRepositoriesService.getAccessToken(depositAuthenticateRequest) + .pipe(takeUntil(this._destroyed)) + .subscribe(token => { + localStorage.setItem('repositoryOauthToken', token); + }, error => this.onCallbackError(error)); + } + + onCallbackError(errorResponse: HttpErrorResponse) { + const error: HttpError = this.httpErrorHandlingService.getError(errorResponse); + this.uiNotificationService.snackBarNotification(error.getMessagesString(), SnackBarNotificationLevel.Warning); + } +} diff --git a/dmp-frontend/src/app/ui/misc/deposit-oauth2-dialog/deposit-oauth2-dialog.module.ts b/dmp-frontend/src/app/ui/misc/deposit-oauth2-dialog/deposit-oauth2-dialog.module.ts new file mode 100644 index 000000000..b770e5de7 --- /dev/null +++ b/dmp-frontend/src/app/ui/misc/deposit-oauth2-dialog/deposit-oauth2-dialog.module.ts @@ -0,0 +1,19 @@ +import { NgModule } from '@angular/core'; + +import { CommonUiModule } from '@common/ui/common-ui.module'; +import { DepositOauth2DialogComponent } from './deposit-oauth2-dialog.component'; +import { DepositOauth2DialogRoutingModule } from './deposit-oauth2-dialog-routing.module'; +import { DepositOauth2DialogService } from './service/deposit-oauth2-dialog.service'; + + +@NgModule({ + declarations: [DepositOauth2DialogComponent], + imports: [ + CommonUiModule, + DepositOauth2DialogRoutingModule, + ], + providers: [ + DepositOauth2DialogService + ] +}) +export class DepositOauth2DialogModule { } diff --git a/dmp-frontend/src/app/ui/misc/oauth2-dialog/service/oauth2-dialog.service.ts b/dmp-frontend/src/app/ui/misc/deposit-oauth2-dialog/service/deposit-oauth2-dialog.service.ts similarity index 55% rename from dmp-frontend/src/app/ui/misc/oauth2-dialog/service/oauth2-dialog.service.ts rename to dmp-frontend/src/app/ui/misc/deposit-oauth2-dialog/service/deposit-oauth2-dialog.service.ts index 8b556b63d..980498f76 100644 --- a/dmp-frontend/src/app/ui/misc/oauth2-dialog/service/oauth2-dialog.service.ts +++ b/dmp-frontend/src/app/ui/misc/deposit-oauth2-dialog/service/deposit-oauth2-dialog.service.ts @@ -4,9 +4,10 @@ import { BehaviorSubject, Observable, interval } from 'rxjs'; import { ConfigurationService } from '@app/core/services/configuration/configuration.service'; import { isNullOrUndefined } from '@app/utilities/enhancers/utils'; import { takeUntil } from 'rxjs/operators'; +import { DepositConfiguration } from '@app/core/model/deposit/deposit-configuration'; @Injectable() -export class Oauth2DialogService extends BaseService{ +export class DepositOauth2DialogService extends BaseService{ private code: BehaviorSubject = new BehaviorSubject(undefined); @@ -14,8 +15,11 @@ export class Oauth2DialogService extends BaseService{ super(); } - public registerCode(code: string) { - this.code.next(code); + public getLoginUrl(repo: DepositConfiguration, entityId) { + return repo.repositoryAuthorizationUrl + '?client_id=' + repo.repositoryClientId + + '&state=' + encodeURIComponent(btoa(JSON.stringify({ repositoryId: repo.repositoryId, entity: entityId }))) + + '&response_type=code&scope=deposit:write+deposit:actions+user:email&redirect_uri=' + + repo.redirectUri } public login(url: string): Observable { @@ -23,24 +27,11 @@ export class Oauth2DialogService extends BaseService{ const sub = interval(300).pipe(takeUntil(this._destroyed)).subscribe(() => { if (windows.closed) { let oauthCode; - let oauthState; - let oauthToken; - let oauthVerifier; - if (localStorage.getItem('oauthCode')) { - oauthCode = localStorage.getItem('oauthCode'); - localStorage.removeItem('oauthCode'); + if (localStorage.getItem('repositoryOauthToken')) { + oauthCode = localStorage.getItem('repositoryOauthToken'); + localStorage.removeItem('repositoryOauthToken'); } - if (localStorage.getItem('oauthState')) { - oauthState = localStorage.getItem('oauthState'); - localStorage.removeItem('oauthState'); - } - if (localStorage.getItem('oauthObject')) { - const oauthObject = JSON.parse(localStorage.getItem('oauthObject')); - localStorage.removeItem('oauthObject'); - oauthToken = oauthObject.oauth_token; - oauthVerifier = oauthObject.oauth_verifier; - } - this.code.next({oauthCode: oauthCode, oauthState: oauthState, oauthToken: oauthToken, oauthVerifier: oauthVerifier}); + this.code.next(oauthCode); this.code.next(undefined); sub.unsubscribe(); } diff --git a/dmp-frontend/src/app/ui/misc/oauth2-dialog/oauth2-dialog.component.ts b/dmp-frontend/src/app/ui/misc/oauth2-dialog/oauth2-dialog.component.ts deleted file mode 100644 index 2ec574631..000000000 --- a/dmp-frontend/src/app/ui/misc/oauth2-dialog/oauth2-dialog.component.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { Component, OnInit, Inject } from '@angular/core'; -import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; -import { ActivatedRoute, Params } from '@angular/router'; -import { BaseComponent } from '@common/base/base.component'; -import { takeUntil } from 'rxjs/operators'; -import { Oauth2DialogService } from './service/oauth2-dialog.service'; - -@Component({ - selector: 'app-oauth2-dialog', - templateUrl: './oauth2-dialog.component.html', - styleUrls: ['./oauth2-dialog.component.scss'] -}) -export class Oauth2DialogComponent extends BaseComponent implements OnInit{ - - constructor( - private route: ActivatedRoute, - private oauth2dialogService: Oauth2DialogService - ) { - super(); - } - - - ngOnInit(): void { - this.route.queryParams.pipe(takeUntil(this._destroyed)) - .subscribe((params: Params) => { - const url = params['url']; - if (!params['code'] && (!params['oauth_token'] && !params['oauth_verifier'])) { this.loadUrl(url) } else { this.sendCode(params); } - }); - } - - private loadUrl(url: string ) { - window.location.href = url; - } - - private sendCode(params: Params) { - if (params['code']) { - localStorage.setItem('oauthCode', params['code']); - } - if (params['state']) { - localStorage.setItem('oauthState', params['state']); - } - if (params['oauth_token'] && params['oauth_verifier']) { - localStorage.setItem('oauthObject', JSON.stringify({oauth_token: params['oauth_token'], oauth_verifier: params['oauth_verifier']})); - } - window.close(); - } - -} diff --git a/dmp-frontend/src/app/ui/misc/oauth2-dialog/oauth2-dialog.module.ts b/dmp-frontend/src/app/ui/misc/oauth2-dialog/oauth2-dialog.module.ts deleted file mode 100644 index 5bdefcec4..000000000 --- a/dmp-frontend/src/app/ui/misc/oauth2-dialog/oauth2-dialog.module.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { NgModule } from '@angular/core'; - -import { Oauth2DialogComponent } from './oauth2-dialog.component'; -import { CommonUiModule } from '@common/ui/common-ui.module'; -import { Oauth2DialogService } from './service/oauth2-dialog.service'; - - -@NgModule({ - declarations: [Oauth2DialogComponent], - imports: [ - CommonUiModule - ], - providers: [ - Oauth2DialogService - ] -}) -export class Oauth2DialogModule { } From 6b69078007d8a2fe1c9f27c2b758d056283c4e9e Mon Sep 17 00:00:00 2001 From: amandilaras Date: Mon, 20 May 2024 12:23:42 +0300 Subject: [PATCH 2/3] comment plugin mounts on deployment --- deployment/docker-compose.override.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/deployment/docker-compose.override.yml b/deployment/docker-compose.override.yml index 511585c3c..8fba06b84 100644 --- a/deployment/docker-compose.override.yml +++ b/deployment/docker-compose.override.yml @@ -105,7 +105,7 @@ services: volumes: - ./opencdmp/file-transformer-docx/config:/config - ./logs/opencdmp/file-transformer-docx:/logs - - ./storage/opencdmp/file-transformer-docx:/storage + # - ./storage/opencdmp/file-transformer-docx:/storage networks: - opencdmp-plugins-network - opencdmp-gotenberg-shared-network @@ -123,7 +123,7 @@ services: - ./opencdmp/file-transformer-rda-json/config:/config - ./opencdmp/file-transformer-rda-json/internal:/internal - ./logs/opencdmp/file-transformer-rda-json:/logs - - ./storage/opencdmp/file-transformer-rda-json:/storage + # - ./storage/opencdmp/file-transformer-rda-json:/storage networks: - opencdmp-plugins-network @@ -139,8 +139,8 @@ services: volumes: - ./opencdmp/zenodo/config:/config - ./opencdmp/zenodo/zenodo.jpg:/zenodo.jpg - - ./storage/opencdmp/zenodo:/storage - ./logs/opencdmp/zenodo:/logs + # - ./storage/opencdmp/zenodo:/storage networks: - opencdmp-plugins-network - opencdmp-keycloak-shared-network From 6023f6fa31036cbfb293dcf6aa724bb5f14fdbad Mon Sep 17 00:00:00 2001 From: amentis Date: Mon, 20 May 2024 13:20:45 +0300 Subject: [PATCH 3/3] add import description, fix import/export models --- ...scriptionTemplateFieldSetImportExport.java | 9 + .../DescriptionTemplateImportExport.java | 49 +++++ .../DescriptionTemplatePageImportExport.java | 20 ++ ...escriptionTemplateSectionImportExport.java | 29 +++ .../dmp/importexport/DmpImportExport.java | 11 ++ .../description/DescriptionService.java | 5 + .../description/DescriptionServiceImpl.java | 177 +++++++++++++++++- .../DescriptionTemplateServiceImpl.java | 3 + .../opencdmp/service/dmp/DmpServiceImpl.java | 20 +- .../opencdmp/controllers/DmpController.java | 2 +- .../src/app/core/services/dmp/dmp.service.ts | 6 + 11 files changed, 323 insertions(+), 8 deletions(-) diff --git a/backend/core/src/main/java/org/opencdmp/commons/types/descriptiontemplate/importexport/DescriptionTemplateFieldSetImportExport.java b/backend/core/src/main/java/org/opencdmp/commons/types/descriptiontemplate/importexport/DescriptionTemplateFieldSetImportExport.java index 880929e52..daa00b07c 100644 --- a/backend/core/src/main/java/org/opencdmp/commons/types/descriptiontemplate/importexport/DescriptionTemplateFieldSetImportExport.java +++ b/backend/core/src/main/java/org/opencdmp/commons/types/descriptiontemplate/importexport/DescriptionTemplateFieldSetImportExport.java @@ -2,6 +2,7 @@ package org.opencdmp.commons.types.descriptiontemplate.importexport; import jakarta.xml.bind.annotation.*; +import java.util.ArrayList; import java.util.List; @XmlAccessorType(XmlAccessType.FIELD) @@ -109,4 +110,12 @@ public class DescriptionTemplateFieldSetImportExport { public void setHasMultiplicity(boolean hasMultiplicity) { this.hasMultiplicity = hasMultiplicity; } + + public List getAllField() { + return this.getFields() == null ? new ArrayList<>() : this.getFields(); + } + + public List getFieldById(String id) { + return this.getAllField().stream().filter(x-> id.equals(x.getId())).toList(); + } } diff --git a/backend/core/src/main/java/org/opencdmp/commons/types/descriptiontemplate/importexport/DescriptionTemplateImportExport.java b/backend/core/src/main/java/org/opencdmp/commons/types/descriptiontemplate/importexport/DescriptionTemplateImportExport.java index 82c61d937..6ba455227 100644 --- a/backend/core/src/main/java/org/opencdmp/commons/types/descriptiontemplate/importexport/DescriptionTemplateImportExport.java +++ b/backend/core/src/main/java/org/opencdmp/commons/types/descriptiontemplate/importexport/DescriptionTemplateImportExport.java @@ -3,6 +3,7 @@ package org.opencdmp.commons.types.descriptiontemplate.importexport; import jakarta.xml.bind.annotation.*; +import java.util.ArrayList; import java.util.List; import java.util.UUID; @@ -18,6 +19,10 @@ public class DescriptionTemplateImportExport { private String language; @XmlAttribute(name = "type") private UUID type; + @XmlAttribute(name = "version") + private Short version; + @XmlElement(name = "groupId") + private UUID groupId; @XmlElementWrapper(name = "pages") @XmlElement(name = "page") private List pages; @@ -64,4 +69,48 @@ public class DescriptionTemplateImportExport { public void setType(UUID type) { this.type = type; } + + public Short getVersion() { + return version; + } + + public void setVersion(Short version) { + this.version = version; + } + + public UUID getGroupId() { + return groupId; + } + + public void setGroupId(UUID groupId) { + this.groupId = groupId; + } + + public List getAllFieldSets(){ + List fieldSets = new ArrayList<>(); + if (this.getPages() != null){ + for (DescriptionTemplatePageImportExport page: this.getPages()) { + fieldSets.addAll(page.getAllFieldSets()); + } + } + return fieldSets; + } + + public List getAllField(){ + List fields = new ArrayList<>(); + if (this.getPages() != null){ + for (DescriptionTemplatePageImportExport page: this.getPages()) { + fields.addAll(page.getAllField()); + } + } + return fields; + } + + public List getFieldSetById(String id) { + return this.getAllFieldSets().stream().filter(x-> id.equals(x.getId())).toList(); + } + + public List getFieldById(String id) { + return this.getAllField().stream().filter(x-> id.equals(x.getId())).toList(); + } } diff --git a/backend/core/src/main/java/org/opencdmp/commons/types/descriptiontemplate/importexport/DescriptionTemplatePageImportExport.java b/backend/core/src/main/java/org/opencdmp/commons/types/descriptiontemplate/importexport/DescriptionTemplatePageImportExport.java index 4833eb7d7..c48fdc230 100644 --- a/backend/core/src/main/java/org/opencdmp/commons/types/descriptiontemplate/importexport/DescriptionTemplatePageImportExport.java +++ b/backend/core/src/main/java/org/opencdmp/commons/types/descriptiontemplate/importexport/DescriptionTemplatePageImportExport.java @@ -2,6 +2,7 @@ package org.opencdmp.commons.types.descriptiontemplate.importexport; import jakarta.xml.bind.annotation.*; +import java.util.ArrayList; import java.util.List; @XmlAccessorType(XmlAccessType.FIELD) @@ -49,4 +50,23 @@ public class DescriptionTemplatePageImportExport { this.sections = sections; } + public List getAllField(){ + List fields = new ArrayList<>(); + if (this.getSections() != null){ + for (DescriptionTemplateSectionImportExport section: this.getSections()) { + fields.addAll(section.getAllField()); + } + } + return fields; + } + + public List getAllFieldSets(){ + List fieldSets = new ArrayList<>(); + if (this.getSections() != null){ + for (DescriptionTemplateSectionImportExport section: this.getSections()) { + fieldSets.addAll(section.getAllFieldSets()); + } + } + return fieldSets; + } } diff --git a/backend/core/src/main/java/org/opencdmp/commons/types/descriptiontemplate/importexport/DescriptionTemplateSectionImportExport.java b/backend/core/src/main/java/org/opencdmp/commons/types/descriptiontemplate/importexport/DescriptionTemplateSectionImportExport.java index a66f76f59..3e227474d 100644 --- a/backend/core/src/main/java/org/opencdmp/commons/types/descriptiontemplate/importexport/DescriptionTemplateSectionImportExport.java +++ b/backend/core/src/main/java/org/opencdmp/commons/types/descriptiontemplate/importexport/DescriptionTemplateSectionImportExport.java @@ -2,6 +2,7 @@ package org.opencdmp.commons.types.descriptiontemplate.importexport; import jakarta.xml.bind.annotation.*; +import java.util.ArrayList; import java.util.List; @XmlAccessorType(XmlAccessType.FIELD) @@ -69,4 +70,32 @@ public class DescriptionTemplateSectionImportExport { this.sections = sections; } + + public List getAllField(){ + List fields = new ArrayList<>(); + if (this.getFieldSets() != null){ + for (DescriptionTemplateFieldSetImportExport fieldSet: this.getFieldSets()) { + fields.addAll(fieldSet.getAllField()); + } + } + if (this.getSections() != null){ + for (DescriptionTemplateSectionImportExport section: this.getSections()) { + fields.addAll(section.getAllField()); + } + } + return fields; + } + + public List getAllFieldSets(){ + List fieldSets = new ArrayList<>(); + if (this.getFieldSets() != null){ + fieldSets.addAll(this.getFieldSets()); + } + if (this.getSections() != null){ + for (DescriptionTemplateSectionImportExport section: this.getSections()) { + fieldSets.addAll(section.getAllFieldSets()); + } + } + return fieldSets; + } } diff --git a/backend/core/src/main/java/org/opencdmp/commons/types/dmp/importexport/DmpImportExport.java b/backend/core/src/main/java/org/opencdmp/commons/types/dmp/importexport/DmpImportExport.java index 2c007cca5..3a6bc7302 100644 --- a/backend/core/src/main/java/org/opencdmp/commons/types/dmp/importexport/DmpImportExport.java +++ b/backend/core/src/main/java/org/opencdmp/commons/types/dmp/importexport/DmpImportExport.java @@ -28,6 +28,9 @@ public class DmpImportExport { @XmlElement(name = "access") private DmpAccessType access; + @XmlElement(name = "version") + private Short version; + @XmlElementWrapper(name = "contacts") @XmlElement(name = "contact") private List contacts; @@ -173,5 +176,13 @@ public class DmpImportExport { public void setDescriptions(List descriptions) { this.descriptions = descriptions; } + + public Short getVersion() { + return version; + } + + public void setVersion(Short version) { + this.version = version; + } } diff --git a/backend/core/src/main/java/org/opencdmp/service/description/DescriptionService.java b/backend/core/src/main/java/org/opencdmp/service/description/DescriptionService.java index 23e5e027b..9fd6abf17 100644 --- a/backend/core/src/main/java/org/opencdmp/service/description/DescriptionService.java +++ b/backend/core/src/main/java/org/opencdmp/service/description/DescriptionService.java @@ -7,6 +7,7 @@ import gr.cite.tools.exception.MyValidationException; import gr.cite.tools.fieldset.FieldSet; import jakarta.xml.bind.JAXBException; import org.opencdmp.commons.types.description.importexport.DescriptionImportExport; +import org.opencdmp.data.DmpDescriptionTemplateEntity; import org.opencdmp.data.StorageFileEntity; import org.opencdmp.model.DescriptionValidationResult; import org.opencdmp.model.StorageFile; @@ -21,6 +22,7 @@ import javax.crypto.IllegalBlockSizeException; import javax.crypto.NoSuchPaddingException; import javax.management.InvalidApplicationException; import javax.xml.parsers.ParserConfigurationException; +import javax.xml.transform.TransformerException; import java.io.IOException; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; @@ -49,4 +51,7 @@ public interface DescriptionService { DescriptionImportExport exportXmlEntity(UUID id, boolean ignoreAuthorize) throws MyForbiddenException, MyNotFoundException, JAXBException, ParserConfigurationException, IOException, InstantiationException, IllegalAccessException, SAXException, InvalidApplicationException; ResponseEntity exportXml(UUID id) throws MyForbiddenException, MyNotFoundException, JAXBException, ParserConfigurationException, IOException, InstantiationException, IllegalAccessException, SAXException, InvalidApplicationException; + + Description importXml(DescriptionImportExport descriptionXml, UUID dmpId, List dmpDescriptionTemplates, FieldSet fields) throws MyForbiddenException, MyNotFoundException, JAXBException, ParserConfigurationException, TransformerException, InvalidApplicationException, IOException, InstantiationException, IllegalAccessException, SAXException; + } diff --git a/backend/core/src/main/java/org/opencdmp/service/description/DescriptionServiceImpl.java b/backend/core/src/main/java/org/opencdmp/service/description/DescriptionServiceImpl.java index e52c2c0ff..e6ab79222 100644 --- a/backend/core/src/main/java/org/opencdmp/service/description/DescriptionServiceImpl.java +++ b/backend/core/src/main/java/org/opencdmp/service/description/DescriptionServiceImpl.java @@ -30,6 +30,8 @@ import org.opencdmp.commons.types.descriptionreference.DescriptionReferenceDataE import org.opencdmp.commons.types.descriptiontemplate.FieldSetEntity; import org.opencdmp.commons.types.descriptiontemplate.fielddata.ReferenceTypeDataEntity; import org.opencdmp.commons.types.descriptiontemplate.fielddata.UploadDataEntity; +import org.opencdmp.commons.types.descriptiontemplate.importexport.*; +import org.opencdmp.commons.types.descriptiontemplate.importexport.fielddata.ReferenceTypeDataImportExport; import org.opencdmp.commons.types.notification.*; import org.opencdmp.commons.types.reference.DefinitionEntity; import org.opencdmp.convention.ConventionService; @@ -86,6 +88,7 @@ import javax.crypto.IllegalBlockSizeException; import javax.crypto.NoSuchPaddingException; import javax.management.InvalidApplicationException; import javax.xml.parsers.ParserConfigurationException; +import javax.xml.transform.TransformerException; import java.io.IOException; import java.net.URLConnection; import java.security.InvalidAlgorithmParameterException; @@ -1096,9 +1099,12 @@ public class DescriptionServiceImpl implements DescriptionService { xml.setLabel(data.getLabel()); xml.setFinalizedAt(data.getFinalizedAt()); - DescriptionTemplateEntity blueprintEntity = this.queryFactory.query(DescriptionTemplateQuery.class).disableTracking().ids(data.getDescriptionTemplateId()).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermissionOrPublic).isActive(IsActive.Active).first(); - if (blueprintEntity != null) { - xml.setDescriptionTemplate(this.descriptionTemplateService.exportXmlEntity(blueprintEntity.getId(), true)); + DmpDescriptionTemplateEntity dmpDescriptionTemplateEntity = this.queryFactory.query(DmpDescriptionTemplateQuery.class).disableTracking().ids(data.getDmpDescriptionTemplateId()).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermissionOrPublic).isActive(IsActive.Active).first(); + if (dmpDescriptionTemplateEntity != null) xml.setSectionId(dmpDescriptionTemplateEntity.getSectionId()); + + DescriptionTemplateEntity descriptionTemplateEntity = this.queryFactory.query(DescriptionTemplateQuery.class).disableTracking().ids(data.getDescriptionTemplateId()).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermissionOrPublic).isActive(IsActive.Active).first(); + if (descriptionTemplateEntity != null) { + xml.setDescriptionTemplate(this.descriptionTemplateService.exportXmlEntity(descriptionTemplateEntity.getId(), true)); } if (propertiesEntity != null) { @@ -1112,8 +1118,8 @@ public class DescriptionServiceImpl implements DescriptionService { List referenceTypes = references == null ? new ArrayList<>() : this.queryFactory.query(ReferenceTypeQuery.class).disableTracking().ids(references.stream().map(ReferenceEntity::getTypeId).distinct().toList()).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermissionOrPublic).isActive(IsActive.Active).collect(); Map referenceTypeEntityMap = referenceTypes == null ? new HashMap<>() : referenceTypes.stream().collect(Collectors.toMap(ReferenceTypeEntity::getId, x-> x)); List dmpReferenceImportExports = new LinkedList<>(); - for (DescriptionReferenceEntity descriptionTemplateEntity : dmpReferences) { - dmpReferenceImportExports.add(this.descriptionReferenceToExport(descriptionTemplateEntity, referenceEntityMap, referenceTypeEntityMap)); + for (DescriptionReferenceEntity descriptionReferenceEntity : dmpReferences) { + dmpReferenceImportExports.add(this.descriptionReferenceToExport(descriptionReferenceEntity, referenceEntityMap, referenceTypeEntityMap)); } xml.setReferences(dmpReferenceImportExports); } @@ -1224,4 +1230,165 @@ public class DescriptionServiceImpl implements DescriptionService { } //endregion + + + //region Import + + public Description importXml(DescriptionImportExport descriptionXml, UUID dmpId, List dmpDescriptionTemplates, FieldSet fields) throws MyForbiddenException, MyNotFoundException, JAXBException, ParserConfigurationException, TransformerException, InvalidApplicationException, IOException, InstantiationException, IllegalAccessException, SAXException{ + + if (descriptionXml == null) throw new MyNotFoundException("Description xml not found"); + + logger.debug(new MapLogEntry("import description").And("dmpId", dmpId).And("fields", fields)); + + DescriptionPersist persist = new DescriptionPersist(); + persist.setLabel(descriptionXml.getLabel()); + persist.setDescription(descriptionXml.getDescription()); + persist.setStatus(DescriptionStatus.Draft); + persist.setDmpId(dmpId); + if (descriptionXml.getDescriptionTemplate() != null) { + persist.setDescriptionTemplateId(descriptionXml.getDescriptionTemplate().getId()); + if (!this.conventionService.isListNullOrEmpty(dmpDescriptionTemplates) && descriptionXml.getSectionId() != null && descriptionXml.getDescriptionTemplate().getGroupId() != null){ + DmpDescriptionTemplateEntity dmpDescriptionTemplate = dmpDescriptionTemplates.stream().filter(x -> x.getDmpId().equals(dmpId) && + x.getDescriptionTemplateGroupId().equals(descriptionXml.getDescriptionTemplate().getGroupId()) && + x.getSectionId().equals(descriptionXml.getSectionId())).findFirst().orElse(null); + if (dmpDescriptionTemplate != null) persist.setDmpDescriptionTemplateId(dmpDescriptionTemplate.getId()); + } + } + + persist.setProperties(this.xmlToPropertyDefinitionToPersist(descriptionXml)); + + this.validatorFactory.validator(DescriptionPersist.DescriptionPersistValidator.class).validateForce(persist); + + return this.persist(persist, fields); + } + + private PropertyDefinitionPersist xmlToPropertyDefinitionToPersist(DescriptionImportExport descriptionXml) { + if (descriptionXml == null) + return null; + + PropertyDefinitionPersist persist = new PropertyDefinitionPersist(); + + Map fieldSetsMap = new HashMap<>(); + if (!this.conventionService.isListNullOrEmpty(descriptionXml.getDescriptionTemplate().getPages())) { + if (descriptionXml.getProperties() != null && !this.conventionService.isListNullOrEmpty(descriptionXml.getProperties().getFieldSets())){ + for (DescriptionPropertyDefinitionFieldSetImportExport fieldSet: descriptionXml.getProperties().getFieldSets()){ + fieldSetsMap.put(fieldSet.getFieldSetId(), this.xmlPropertyDefinitionFieldSetToPersist(fieldSet, descriptionXml.getReferences(), descriptionXml.getDescriptionTemplate())); + } + } + } + + persist.setFieldSets(fieldSetsMap); + + return persist; + } + + private PropertyDefinitionFieldSetPersist xmlPropertyDefinitionFieldSetToPersist(DescriptionPropertyDefinitionFieldSetImportExport importXml, List references, DescriptionTemplateImportExport descriptionTemplate) { + + if (importXml == null) + return null; + + PropertyDefinitionFieldSetPersist persist = new PropertyDefinitionFieldSetPersist(); + + if (!this.conventionService.isListNullOrEmpty(importXml.getItems())){ + List items = new ArrayList<>(); + for (DescriptionPropertyDefinitionFieldSetItemImportExport fieldSetItem: importXml.getItems()) { + items.add(this.xmlPropertyDefinitionFieldSetItemToPersist(fieldSetItem, references, descriptionTemplate)); + } + persist.setItems(items); + return persist; + + } + return null; + } + + private PropertyDefinitionFieldSetItemPersist xmlPropertyDefinitionFieldSetItemToPersist(DescriptionPropertyDefinitionFieldSetItemImportExport importXml, List references, DescriptionTemplateImportExport descriptionTemplate) { + if (importXml == null) + return null; + + PropertyDefinitionFieldSetItemPersist persist = new PropertyDefinitionFieldSetItemPersist(); + + persist.setComment(importXml.getComment()); + persist.setOrdinal(importXml.getOrdinal()); + + Map fields = new HashMap<>(); + + if (!this.conventionService.isListNullOrEmpty(importXml.getFields())){ + for (DescriptionFieldImportExport field: importXml.getFields()) { + fields.put(field.getFieldId(), this.xmlFieldToPersist(field, references, descriptionTemplate)); + } + } + + persist.setFields(fields); + + return persist; + } + + private FieldPersist xmlFieldToPersist(DescriptionFieldImportExport importXml, List references, DescriptionTemplateImportExport descriptionTemplate) { + if (importXml == null || descriptionTemplate == null) + return null; + + DescriptionTemplateFieldImportExport descriptionTemplateField = descriptionTemplate.getFieldById(importXml.getFieldId()).stream().findFirst().orElse(null); + if (descriptionTemplateField == null){ + return null; + } + + FieldPersist persist = new FieldPersist(); + + if (!this.conventionService.isListNullOrEmpty(references) && descriptionTemplateField.getData().getFieldType().equals(FieldType.REFERENCE_TYPES)){ + ReferenceTypeDataImportExport referenceTypeDataImportExport = (ReferenceTypeDataImportExport) descriptionTemplateField.getData(); + if (referenceTypeDataImportExport != null){ + List referencesByField = references.stream().filter(x -> x.getFieldId().equals(importXml.getFieldId())).collect(Collectors.toList()); + if (!this.conventionService.isListNullOrEmpty(referencesByField)){ + if (referenceTypeDataImportExport.getMultipleSelect()){ + List referencePersists = new ArrayList<>(); + for (DescriptionReferenceImportExport referenceImportExport: referencesByField) { + referencePersists.add(this.xmlDescriptionReferenceToPersist(referenceImportExport)); + } + persist.setReferences(referencePersists); + }else { + persist.setReference(this.xmlDescriptionReferenceToPersist(referencesByField.stream().findFirst().orElse(null))); + } + } + } + }else { + persist.setBooleanValue(importXml.getBooleanValue()); + persist.setDateValue(importXml.getDateValue()); + persist.setTextValue(importXml.getTextValue()); + persist.setTextListValue(importXml.getTextListValue()); + + if (importXml.getExternalIdentifier() != null){ + persist.setExternalIdentifier(this.xmlExternalIdentifierToPersist(importXml.getExternalIdentifier())); + } + } + + return persist; + } + + private ReferencePersist xmlDescriptionReferenceToPersist(DescriptionReferenceImportExport importXml) { + if (importXml == null) + return null; + + ReferencePersist persist = new ReferencePersist(); + + persist.setId(importXml.getId()); + persist.setLabel(importXml.getLabel()); + persist.setReference(importXml.getReference()); + + return persist; + } + + private ExternalIdentifierPersist xmlExternalIdentifierToPersist(DescriptionExternalIdentifierImportExport importXml) { + if (importXml == null) + return null; + + ExternalIdentifierPersist persist = new ExternalIdentifierPersist(); + + persist.setType(importXml.getType()); + persist.setIdentifier(importXml.getIdentifier()); + + return persist; + } + + //endregion + } diff --git a/backend/core/src/main/java/org/opencdmp/service/descriptiontemplate/DescriptionTemplateServiceImpl.java b/backend/core/src/main/java/org/opencdmp/service/descriptiontemplate/DescriptionTemplateServiceImpl.java index 593d08005..f31f880c7 100644 --- a/backend/core/src/main/java/org/opencdmp/service/descriptiontemplate/DescriptionTemplateServiceImpl.java +++ b/backend/core/src/main/java/org/opencdmp/service/descriptiontemplate/DescriptionTemplateServiceImpl.java @@ -926,6 +926,9 @@ public class DescriptionTemplateServiceImpl implements DescriptionTemplateServic xml.setType(data.getTypeId()); xml.setLanguage(data.getLanguage()); xml.setDescription(data.getDescription()); + xml.setGroupId(data.getGroupId()); + xml.setVersion(data.getVersion()); + List pagesDatasetEntity = new LinkedList<>(); for (PageEntity xmlPage : entity.getPages()) { pagesDatasetEntity.add(this.pageXmlToExport(xmlPage)); diff --git a/backend/core/src/main/java/org/opencdmp/service/dmp/DmpServiceImpl.java b/backend/core/src/main/java/org/opencdmp/service/dmp/DmpServiceImpl.java index cb6a00058..2e299b056 100644 --- a/backend/core/src/main/java/org/opencdmp/service/dmp/DmpServiceImpl.java +++ b/backend/core/src/main/java/org/opencdmp/service/dmp/DmpServiceImpl.java @@ -1477,7 +1477,8 @@ public class DmpServiceImpl implements DmpService { xml.setAccess(data.getAccessType()); xml.setFinalizedAt(data.getFinalizedAt()); xml.setPublicAfter(data.getPublicAfter()); - + xml.setVersion(data.getVersion()); + if (propertiesEntity != null && !this.conventionService.isListNullOrEmpty(propertiesEntity.getContacts())) { List dmpContactImportExports = new LinkedList<>(); List users = this.queryFactory.query(UserQuery.class).disableTracking().ids(propertiesEntity.getContacts().stream().map(DmpContactEntity::getUserId).filter(Objects::nonNull).distinct().toList()).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermissionOrPublic).isActive(IsActive.Active).collect(); @@ -1668,7 +1669,22 @@ public class DmpServiceImpl implements DmpService { this.validatorFactory.validator(DmpPersist.DmpPersistValidator.class).validateForce(persist); - return this.persist(persist, fields); + Dmp dmp = this.persist(persist, fields); + + if (!this.conventionService.isListNullOrEmpty(dmpXml.getDescriptions())){ + if (dmp == null || dmp.getId() == null) throw new MyApplicationException("Error creating dmp"); + + List dmpDescriptionTemplates = this.queryFactory.query(DmpDescriptionTemplateQuery.class).disableTracking() + .isActive(IsActive.Active) + .dmpIds(dmp.getId()) + .collect(); + + for (DescriptionImportExport description: dmpXml.getDescriptions()){ + this.descriptionService.importXml(description, dmp.getId(), dmpDescriptionTemplates, fields != null ? fields.extractPrefixed(this.conventionService.asPrefix(Dmp._description)) : null); + } + } + + return dmp; } private DmpPropertiesPersist xmlToDmpPropertiesPersist(DmpImportExport importXml) { diff --git a/backend/web/src/main/java/org/opencdmp/controllers/DmpController.java b/backend/web/src/main/java/org/opencdmp/controllers/DmpController.java index 0a9244486..b50f254ee 100644 --- a/backend/web/src/main/java/org/opencdmp/controllers/DmpController.java +++ b/backend/web/src/main/java/org/opencdmp/controllers/DmpController.java @@ -340,7 +340,7 @@ public class DmpController { @RequestMapping(method = RequestMethod.GET, value = "/xml/export/{id}", produces = "application/xml") public @ResponseBody ResponseEntity getXml(@PathVariable UUID id) throws JAXBException, ParserConfigurationException, IOException, InstantiationException, IllegalAccessException, SAXException, InvalidApplicationException { - logger.debug(new MapLogEntry("export" + DmpBlueprint.class.getSimpleName()).And("id", id)); + logger.debug(new MapLogEntry("export" + Dmp.class.getSimpleName()).And("id", id)); ResponseEntity response = this.dmpService.exportXml(id); diff --git a/dmp-frontend/src/app/core/services/dmp/dmp.service.ts b/dmp-frontend/src/app/core/services/dmp/dmp.service.ts index 730ec4052..1457a3677 100644 --- a/dmp-frontend/src/app/core/services/dmp/dmp.service.ts +++ b/dmp-frontend/src/app/core/services/dmp/dmp.service.ts @@ -197,6 +197,12 @@ export class DmpService { const formData = new FormData(); formData.append('file', file); formData.append('label', label); + if (reqFields.length > 0){ + for (var i = 0; i < reqFields.length; i++) { + formData.append('field[]', reqFields[i]); + } + } + return this.http.post(url, formData, { params: params }).pipe( catchError((error: any) => throwError(error)));; }