file transformer changes
This commit is contained in:
parent
4e61353f77
commit
704c9f2018
|
@ -48,6 +48,7 @@ public class DescriptionCommonModelBuilder extends BaseCommonModelBuilder<Descri
|
|||
private final XmlHandlingService xmlHandlingService;
|
||||
|
||||
private EnumSet<AuthorizationFlags> authorize = EnumSet.of(AuthorizationFlags.None);
|
||||
private String repositoryId;
|
||||
|
||||
@Autowired
|
||||
public DescriptionCommonModelBuilder(
|
||||
|
@ -73,6 +74,11 @@ public class DescriptionCommonModelBuilder extends BaseCommonModelBuilder<Descri
|
|||
return this;
|
||||
}
|
||||
|
||||
public DescriptionCommonModelBuilder setRepositoryId(String repositoryId) {
|
||||
this.repositoryId = repositoryId;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<CommonModelBuilderItemResponse<DescriptionModel, DescriptionEntity>> buildInternal(List<DescriptionEntity> data) throws MyApplicationException {
|
||||
this.logger.debug("building for {}", Optional.ofNullable(data).map(List::size).orElse(0));
|
||||
|
@ -154,7 +160,7 @@ public class DescriptionCommonModelBuilder extends BaseCommonModelBuilder<Descri
|
|||
|
||||
Map<UUID, DmpModel> itemMap;
|
||||
DmpQuery q = this.queryFactory.query(DmpQuery.class).authorize(this.authorize).ids(data.stream().map(DescriptionEntity::getDmpId).distinct().collect(Collectors.toList()));
|
||||
itemMap = this.builderFactory.builder(DmpCommonModelBuilder.class).useSharedStorage(useSharedStorage).setDisableDescriptions(true).authorize(this.authorize).asForeignKey(q, DmpEntity::getId);
|
||||
itemMap = this.builderFactory.builder(DmpCommonModelBuilder.class).setRepositoryId(repositoryId).useSharedStorage(useSharedStorage).setDisableDescriptions(true).authorize(this.authorize).asForeignKey(q, DmpEntity::getId);
|
||||
|
||||
return itemMap;
|
||||
}
|
||||
|
|
|
@ -149,7 +149,7 @@ public class FieldCommonModelBuilder extends BaseCommonModelBuilder<FieldModel,
|
|||
|
||||
Map<UUID, ReferenceModel> itemMap;
|
||||
|
||||
ReferenceQuery q = this.queryFactory.query(ReferenceQuery.class).authorize(this.authorize).ids(data.stream().map(FieldEntity::getTextListValue).flatMap(List::stream).map(UUID::fromString).distinct().collect(Collectors.toList()));
|
||||
ReferenceQuery q = this.queryFactory.query(ReferenceQuery.class).authorize(this.authorize).ids(data.stream().filter(x-> x.getTextListValue() != null).map(FieldEntity::getTextListValue).flatMap(List::stream).filter(x-> !this.conventionService.isNullOrEmpty(x)).map(UUID::fromString).distinct().collect(Collectors.toList()));
|
||||
itemMap = this.builderFactory.builder(ReferenceCommonModelBuilder.class).authorize(this.authorize).asForeignKey(q, ReferenceEntity::getId);
|
||||
|
||||
return itemMap;
|
||||
|
|
|
@ -195,7 +195,7 @@ public class DmpCommonModelBuilder extends BaseCommonModelBuilder<DmpModel, DmpE
|
|||
|
||||
Map<UUID, List<DescriptionModel>> itemMap;
|
||||
DescriptionQuery query = this.queryFactory.query(DescriptionQuery.class).isActive(IsActive.Active).authorize(this.authorize).dmpIds(data.stream().map(DmpEntity::getId).distinct().collect(Collectors.toList()));
|
||||
itemMap = this.builderFactory.builder(DescriptionCommonModelBuilder.class).useSharedStorage(useSharedStorage).authorize(this.authorize).asMasterKey(query, DescriptionEntity::getDmpId);
|
||||
itemMap = this.builderFactory.builder(DescriptionCommonModelBuilder.class).setRepositoryId(repositoryId).useSharedStorage(useSharedStorage).authorize(this.authorize).asMasterKey(query, DescriptionEntity::getDmpId);
|
||||
|
||||
return itemMap;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
package eu.eudat.model.file;
|
||||
|
||||
import eu.eudat.file.transformer.models.misc.FileFormat;
|
||||
|
||||
public class RepositoryFileFormat {
|
||||
private String format;
|
||||
private Boolean hasLogo;
|
||||
private String icon;
|
||||
private String repositoryId;
|
||||
|
||||
public RepositoryFileFormat(String repositoryId, FileFormat format) {
|
||||
this.repositoryId = repositoryId;
|
||||
this.format = format.getFormat();
|
||||
this.hasLogo = format.getHasLogo();
|
||||
this.icon = format.getIcon();
|
||||
}
|
||||
|
||||
public String getFormat() {
|
||||
return format;
|
||||
}
|
||||
|
||||
public void setFormat(String format) {
|
||||
this.format = format;
|
||||
}
|
||||
|
||||
public Boolean getHasLogo() {
|
||||
return hasLogo;
|
||||
}
|
||||
|
||||
public void setHasLogo(Boolean hasLogo) {
|
||||
this.hasLogo = hasLogo;
|
||||
}
|
||||
|
||||
public String getIcon() {
|
||||
return icon;
|
||||
}
|
||||
|
||||
public void setIcon(String icon) {
|
||||
this.icon = icon;
|
||||
}
|
||||
|
||||
public String getRepositoryId() {
|
||||
return repositoryId;
|
||||
}
|
||||
|
||||
public void setRepositoryId(String repositoryId) {
|
||||
this.repositoryId = repositoryId;
|
||||
}
|
||||
}
|
|
@ -349,6 +349,8 @@ public class DescriptionTemplateQuery extends QueryBase<DescriptionTemplateEntit
|
|||
return DescriptionTemplateEntity._label;
|
||||
else if (item.prefix(DescriptionTemplate._definition))
|
||||
return DescriptionTemplateEntity._definition;
|
||||
else if (item.match(DescriptionTemplate._definition))
|
||||
return DescriptionTemplateEntity._definition;
|
||||
else if (item.prefix(DescriptionTemplate._users))
|
||||
return DescriptionTemplateEntity._id;
|
||||
else if (item.match(DescriptionTemplate._description))
|
||||
|
|
|
@ -6,6 +6,8 @@ import eu.eudat.commonmodels.models.dmp.DmpModel;
|
|||
import eu.eudat.file.transformer.interfaces.FileTransformerClient;
|
||||
import eu.eudat.file.transformer.interfaces.FileTransformerConfiguration;
|
||||
import org.springframework.core.ParameterizedTypeReference;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
|
||||
|
||||
|
@ -19,27 +21,27 @@ public class TransformerRepository implements FileTransformerClient {
|
|||
|
||||
@Override
|
||||
public FileEnvelopeModel exportDmp(DmpModel dmpModel, String format) {
|
||||
return transformerClient.post().uri("/export/dmp", uriBuilder -> uriBuilder.queryParam("format", format).build()).bodyValue(dmpModel).exchangeToMono(mono -> mono.bodyToMono(FileEnvelopeModel.class)).block();
|
||||
return transformerClient.post().uri("/export/dmp", uriBuilder -> uriBuilder.queryParam("format", format).build()).bodyValue(dmpModel).header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE).accept(MediaType.APPLICATION_JSON).exchangeToMono(mono -> mono.bodyToMono(FileEnvelopeModel.class)).block();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DmpModel importDmp(FileEnvelopeModel fileEnvelope) {
|
||||
return transformerClient.post().uri("/import/dmp").bodyValue(fileEnvelope).exchangeToMono(mono -> mono.bodyToMono(DmpModel.class)).block();
|
||||
return transformerClient.post().uri("/import/dmp").bodyValue(fileEnvelope).header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE).accept(MediaType.APPLICATION_JSON).exchangeToMono(mono -> mono.bodyToMono(DmpModel.class)).block();
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileEnvelopeModel exportDescription(DescriptionModel descriptionModel, String format) {
|
||||
return transformerClient.post().uri("/export/description", uriBuilder -> uriBuilder.queryParam("format", format).build()).bodyValue(descriptionModel).exchangeToMono(mono -> mono.bodyToMono(FileEnvelopeModel.class)).block();
|
||||
return transformerClient.post().uri("/export/description", uriBuilder -> uriBuilder.queryParam("format", format).build()).bodyValue(descriptionModel).header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE).accept(MediaType.APPLICATION_JSON).exchangeToMono(mono -> mono.bodyToMono(FileEnvelopeModel.class)).block();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DescriptionModel importDescription(FileEnvelopeModel fileEnvelope) {
|
||||
return transformerClient.post().uri("/import/description").bodyValue(fileEnvelope).exchangeToMono(mono -> mono.bodyToMono(DescriptionModel.class)).block();
|
||||
return transformerClient.post().uri("/import/description").bodyValue(fileEnvelope).header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE).accept(MediaType.APPLICATION_JSON).exchangeToMono(mono -> mono.bodyToMono(DescriptionModel.class)).block();
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileTransformerConfiguration getConfiguration() {
|
||||
return transformerClient.get().uri("/formats").exchangeToMono(mono -> mono.bodyToMono(new ParameterizedTypeReference<FileTransformerConfiguration>() {})).block();
|
||||
return transformerClient.get().uri("/formats").header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE).accept(MediaType.APPLICATION_JSON).exchangeToMono(mono -> mono.bodyToMono(new ParameterizedTypeReference<FileTransformerConfiguration>() {})).block();
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -3,6 +3,8 @@ package eu.eudat.service.deposit;
|
|||
import eu.eudat.depositinterface.repository.DepositClient;
|
||||
import eu.eudat.depositinterface.repository.DepositConfiguration;
|
||||
import org.springframework.core.ParameterizedTypeReference;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
import eu.eudat.commonmodels.models.dmp.DmpModel;
|
||||
|
||||
|
@ -17,21 +19,21 @@ public class DepositClientImpl implements DepositClient {
|
|||
|
||||
@Override
|
||||
public String deposit(DmpModel dmpDepositModel, String repositoryAccessToken) throws Exception {
|
||||
return depositClient.post().uri("/", uriBuilder -> uriBuilder.queryParam("authToken", repositoryAccessToken).build()).bodyValue(dmpDepositModel).exchangeToMono(mono -> mono.bodyToMono(String.class)).block();
|
||||
return depositClient.post().uri("/", uriBuilder -> uriBuilder.queryParam("authToken", repositoryAccessToken).build()).bodyValue(dmpDepositModel).header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE).accept(MediaType.APPLICATION_JSON).exchangeToMono(mono -> mono.bodyToMono(String.class)).block();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String authenticate(String code) {
|
||||
return depositClient.get().uri("/authenticate/", uriBuilder -> uriBuilder.queryParam("authToken", code).build()).exchangeToMono(mono -> mono.bodyToMono(String.class)).block();
|
||||
return depositClient.get().uri("/authenticate/", uriBuilder -> uriBuilder.queryParam("authToken", code).build()).header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE).accept(MediaType.APPLICATION_JSON).exchangeToMono(mono -> mono.bodyToMono(String.class)).block();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DepositConfiguration getConfiguration() {
|
||||
return depositClient.get().uri("/configuration").exchangeToMono(mono -> mono.bodyToMono(new ParameterizedTypeReference<DepositConfiguration>() {})).block();
|
||||
return depositClient.get().uri("/configuration").header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE).accept(MediaType.APPLICATION_JSON).exchangeToMono(mono -> mono.bodyToMono(new ParameterizedTypeReference<DepositConfiguration>() {})).block();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLogo() {
|
||||
return depositClient.get().uri("/logo/").exchangeToMono(mono -> mono.bodyToMono(String.class)).block();
|
||||
return depositClient.get().uri("/logo/").header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE).accept(MediaType.APPLICATION_JSON).exchangeToMono(mono -> mono.bodyToMono(String.class)).block();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -58,7 +58,6 @@ public class DepositServiceImpl implements DepositService {
|
|||
private final Map<String, DepositClient> clients;
|
||||
private final TokenExchangeCacheService tokenExchangeCacheService;
|
||||
private final AuthorizationService authorizationService;
|
||||
private final WebClient.Builder webClientBuilder;
|
||||
private final EntityDoiService doiService;
|
||||
private final QueryFactory queryFactory;
|
||||
private final MessageSource messageSource;
|
||||
|
@ -69,21 +68,18 @@ public class DepositServiceImpl implements DepositService {
|
|||
private final UserScope userScope;
|
||||
private final ValidatorFactory validatorFactory;
|
||||
private final StorageFileProperties storageFileProperties;
|
||||
private final ConventionService conventionService;
|
||||
private final AuthorizationContentResolver authorizationContentResolver;
|
||||
@Autowired
|
||||
public DepositServiceImpl(DepositProperties depositProperties,
|
||||
TokenExchangeCacheService tokenExchangeCacheService,
|
||||
WebClient.Builder builder,
|
||||
AuthorizationService authorizationService,
|
||||
EntityDoiService doiService,
|
||||
QueryFactory queryFactory,
|
||||
MessageSource messageSource,
|
||||
BuilderFactory builderFactory, DepositConfigurationCacheService depositConfigurationCacheService, FileTransformerService fileTransformerService, StorageFileService storageFileService, UserScope userScope, ValidatorFactory validatorFactory, StorageFileProperties storageFileProperties, ConventionService conventionService, AuthorizationContentResolver authorizationContentResolver) {
|
||||
BuilderFactory builderFactory, DepositConfigurationCacheService depositConfigurationCacheService, FileTransformerService fileTransformerService, StorageFileService storageFileService, UserScope userScope, ValidatorFactory validatorFactory, StorageFileProperties storageFileProperties, AuthorizationContentResolver authorizationContentResolver) {
|
||||
this.depositProperties = depositProperties;
|
||||
this.tokenExchangeCacheService = tokenExchangeCacheService;
|
||||
this.authorizationService = authorizationService;
|
||||
this.webClientBuilder = builder;
|
||||
this.doiService = doiService;
|
||||
this.queryFactory = queryFactory;
|
||||
this.messageSource = messageSource;
|
||||
|
@ -94,7 +90,6 @@ public class DepositServiceImpl implements DepositService {
|
|||
this.userScope = userScope;
|
||||
this.validatorFactory = validatorFactory;
|
||||
this.storageFileProperties = storageFileProperties;
|
||||
this.conventionService = conventionService;
|
||||
this.authorizationContentResolver = authorizationContentResolver;
|
||||
this.clients = new HashMap<>();
|
||||
}
|
||||
|
@ -108,7 +103,7 @@ public class DepositServiceImpl implements DepositService {
|
|||
String host = URI.create(source.getUrl()).getHost();
|
||||
TokenExchangeModel tokenExchangeModel = new TokenExchangeModel("deposit:" + source.getRepositoryId(), source.getIssuerUrl(), source.getClientId(), source.getClientSecret(), source.getScope());
|
||||
TokenExchangeFilterFunction apiKeyExchangeFilterFunction = new TokenExchangeFilterFunction(this.tokenExchangeCacheService, tokenExchangeModel);
|
||||
WebClient webClient = webClientBuilder.baseUrl(source.getUrl() + "/api/deposit").filters(exchangeFilterFunctions -> exchangeFilterFunctions.add(apiKeyExchangeFilterFunction)).build();
|
||||
WebClient webClient = WebClient.builder().baseUrl(source.getUrl() + "/api/deposit").filters(exchangeFilterFunctions -> exchangeFilterFunctions.add(apiKeyExchangeFilterFunction)).build();
|
||||
DepositClientImpl repository = new DepositClientImpl(webClient);
|
||||
this.clients.put(source.getRepositoryId(), repository);
|
||||
return repository;
|
||||
|
|
|
@ -780,7 +780,7 @@ public class DescriptionServiceImpl implements DescriptionService {
|
|||
public ResponseEntity<byte[]> export(UUID id, String exportType) throws InvalidApplicationException, IOException {
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
|
||||
FileEnvelope fileEnvelope = this.fileTransformerService.exportDescription(id, exportType);
|
||||
FileEnvelope fileEnvelope = this.fileTransformerService.exportDescription(id, null, exportType); //TODO get repo from config
|
||||
headers.add("Content-Disposition", "attachment;filename=" + fileEnvelope.getFilename());
|
||||
byte[] data = fileEnvelope.getFile();
|
||||
headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
package eu.eudat.service.transformer;
|
||||
|
||||
import eu.eudat.file.transformer.models.misc.FileFormat;
|
||||
import eu.eudat.model.file.RepositoryFileFormat;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
public interface FileTransformerService {
|
||||
List<FileFormat> getAvailableExportFileFormats();
|
||||
List<RepositoryFileFormat> getAvailableExportFileFormats();
|
||||
|
||||
eu.eudat.model.file.FileEnvelope exportDmp(UUID dmpId, String repositoryId, String format);
|
||||
|
||||
eu.eudat.model.file.FileEnvelope exportDescription(UUID descriptionId, String format);
|
||||
eu.eudat.model.file.FileEnvelope exportDescription(UUID descriptionId, String repositoryId, String format);
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ import eu.eudat.model.Description;
|
|||
import eu.eudat.model.Dmp;
|
||||
import eu.eudat.model.builder.commonmodels.description.DescriptionCommonModelBuilder;
|
||||
import eu.eudat.model.builder.commonmodels.dmp.DmpCommonModelBuilder;
|
||||
import eu.eudat.model.file.RepositoryFileFormat;
|
||||
import eu.eudat.model.file.TransformerCacheModel;
|
||||
import eu.eudat.query.DescriptionQuery;
|
||||
import eu.eudat.query.DmpQuery;
|
||||
|
@ -47,20 +48,18 @@ public class FileTransformerServiceImpl implements FileTransformerService {
|
|||
private final TokenExchangeCacheService tokenExchangeCacheService;
|
||||
private final FileTransformerConfigurationCache fileTransformerConfigurationCache;
|
||||
private final AuthorizationService authorizationService;
|
||||
private final WebClient.Builder webClientBuilder;
|
||||
private final QueryFactory queryFactory;
|
||||
private final BuilderFactory builderFactory;
|
||||
private final StorageFileService storageFileService;
|
||||
private final MessageSource messageSource;
|
||||
|
||||
@Autowired
|
||||
public FileTransformerServiceImpl(TransformerProperties transformerProperties, TokenExchangeCacheService tokenExchangeCacheService, FileTransformerConfigurationCache fileTransformerConfigurationCache, WebClient.Builder builder, AuthorizationService authorizationService,
|
||||
public FileTransformerServiceImpl(TransformerProperties transformerProperties, TokenExchangeCacheService tokenExchangeCacheService, FileTransformerConfigurationCache fileTransformerConfigurationCache, AuthorizationService authorizationService,
|
||||
QueryFactory queryFactory, BuilderFactory builderFactory, StorageFileService storageFileService, MessageSource messageSource) {
|
||||
this.transformerProperties = transformerProperties;
|
||||
this.tokenExchangeCacheService = tokenExchangeCacheService;
|
||||
this.fileTransformerConfigurationCache = fileTransformerConfigurationCache;
|
||||
this.authorizationService = authorizationService;
|
||||
this.webClientBuilder = builder;
|
||||
this.queryFactory = queryFactory;
|
||||
this.builderFactory = builderFactory;
|
||||
this.storageFileService = storageFileService;
|
||||
|
@ -72,16 +71,17 @@ public class FileTransformerServiceImpl implements FileTransformerService {
|
|||
if (this.clients.containsKey(repoId)) return this.clients.get(repoId);
|
||||
|
||||
//GK: It's register time
|
||||
TransformerProperties.TransformerSource source = transformerProperties.getSources().stream().filter(depositSource -> depositSource.getCodes().contains(repoId)).findFirst().orElse(null);
|
||||
TransformerProperties.TransformerSource source = transformerProperties.getSources().stream().filter(depositSource -> depositSource.getTransformerId().equals(repoId)).findFirst().orElse(null);
|
||||
if (source != null) {
|
||||
String host = URI.create(source.getUrl()).getHost();
|
||||
TokenExchangeModel tokenExchangeModel = new TokenExchangeModel(host + "_" + source.getClientId(), source.getIssuerUrl(), source.getClientId(), source.getClientSecret(), source.getScope());
|
||||
TokenExchangeFilterFunction tokenExchangeFilterFunction = new TokenExchangeFilterFunction(this.tokenExchangeCacheService, tokenExchangeModel);
|
||||
TransformerRepository repository = new TransformerRepository(webClientBuilder.baseUrl(source.getUrl() + "/api/file-transformer").filters(exchangeFilterFunctions -> {
|
||||
|
||||
TransformerRepository repository = new TransformerRepository(WebClient.builder().baseUrl(source.getUrl() + "/api/file-transformer").filters(exchangeFilterFunctions -> {
|
||||
exchangeFilterFunctions.add(tokenExchangeFilterFunction);
|
||||
exchangeFilterFunctions.add(logRequest());
|
||||
}).build());
|
||||
source.getCodes().forEach(code -> this.clients.put(code, repository));
|
||||
this.clients.put(source.getTransformerId(), repository);
|
||||
return repository;
|
||||
}
|
||||
return null;
|
||||
|
@ -89,12 +89,12 @@ public class FileTransformerServiceImpl implements FileTransformerService {
|
|||
|
||||
|
||||
@Override
|
||||
public List<FileFormat> getAvailableExportFileFormats() {
|
||||
List<FileFormat> formats = new ArrayList<>();
|
||||
public List<RepositoryFileFormat> getAvailableExportFileFormats() {
|
||||
List<RepositoryFileFormat> formats = new ArrayList<>();
|
||||
List<FileTransformerConfiguration> configurations = this.getAvailableConfigurations();
|
||||
if(configurations != null){
|
||||
for (FileTransformerConfiguration configuration : configurations){
|
||||
if (configuration != null && configuration.getExportVariants() != null) formats.addAll(configuration.getExportVariants());
|
||||
if (configuration != null && configuration.getExportVariants() != null) formats.addAll(configuration.getExportVariants().stream().map(x-> new RepositoryFileFormat(configuration.getFileTransformerId(), x)).toList());
|
||||
}
|
||||
}
|
||||
return formats;
|
||||
|
@ -105,7 +105,7 @@ public class FileTransformerServiceImpl implements FileTransformerService {
|
|||
if (configs == null) {
|
||||
List<FileTransformerConfiguration> configurations = new ArrayList<>();
|
||||
//GK: So much for lazy loading
|
||||
List<TransformerRepository> repositories = transformerProperties.getSources().stream().map(depositSource -> getRepository(depositSource.getCodes().getFirst())).toList();
|
||||
List<TransformerRepository> repositories = transformerProperties.getSources().stream().map(depositSource -> getRepository(depositSource.getTransformerId())).toList();
|
||||
|
||||
repositories = new ArrayList<>(repositories);
|
||||
repositories.forEach((client) -> {
|
||||
|
@ -131,10 +131,10 @@ public class FileTransformerServiceImpl implements FileTransformerService {
|
|||
this.authorizationService.authorize(Permission.EditDmp);
|
||||
//GK: First get the right client
|
||||
TransformerRepository repository = getRepository(repositoryId);
|
||||
if (repository == null) throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{repositoryId, TransformerRepository.class.getSimpleName()}, LocaleContextHolder.getLocale()));
|
||||
if (repository == null) throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{format, TransformerRepository.class.getSimpleName()}, LocaleContextHolder.getLocale()));
|
||||
//GK: Second get the Target Data Management Plan
|
||||
DmpQuery query = this.queryFactory.query(DmpQuery.class).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermission).ids(dmpId);
|
||||
DmpModel dmpFileTransformerModel = this.builderFactory.builder(DmpCommonModelBuilder.class).useSharedStorage(repository.getConfiguration().isUseSharedStorage()).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermission).build(query.first());
|
||||
DmpModel dmpFileTransformerModel = this.builderFactory.builder(DmpCommonModelBuilder.class).useSharedStorage(repository.getConfiguration().isUseSharedStorage()).setRepositoryId(repository.getConfiguration().getFileTransformerId()).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermission).build(query.first());
|
||||
if (dmpFileTransformerModel == null) throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{dmpId, Dmp.class.getSimpleName()}, LocaleContextHolder.getLocale()));
|
||||
|
||||
FileEnvelopeModel fileEnvelope = repository.exportDmp(dmpFileTransformerModel, format);
|
||||
|
@ -147,15 +147,15 @@ public class FileTransformerServiceImpl implements FileTransformerService {
|
|||
}
|
||||
|
||||
@Override
|
||||
public eu.eudat.model.file.FileEnvelope exportDescription(UUID descriptionId, String format) {
|
||||
public eu.eudat.model.file.FileEnvelope exportDescription(UUID descriptionId, String repositoryId, String format) {
|
||||
this.authorizationService.authorize(Permission.EditDmp);
|
||||
//GK: First get the right client
|
||||
TransformerRepository repository = getRepository(format);
|
||||
TransformerRepository repository = getRepository(repositoryId);
|
||||
if (repository == null) throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{format, TransformerRepository.class.getSimpleName()}, LocaleContextHolder.getLocale()));
|
||||
|
||||
//GK: Second get the Target Data Management Plan
|
||||
DescriptionQuery query = this.queryFactory.query(DescriptionQuery.class).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermission).ids(descriptionId);
|
||||
DescriptionModel descriptionFileTransformerModel = this.builderFactory.builder(DescriptionCommonModelBuilder.class).useSharedStorage(repository.getConfiguration().isUseSharedStorage()).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermission).build(query.first());
|
||||
DescriptionModel descriptionFileTransformerModel = this.builderFactory.builder(DescriptionCommonModelBuilder.class).setRepositoryId(repository.getConfiguration().getFileTransformerId()).useSharedStorage(repository.getConfiguration().isUseSharedStorage()).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermission).build(query.first());
|
||||
if (descriptionFileTransformerModel == null) throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{descriptionId, Description.class.getSimpleName()}, LocaleContextHolder.getLocale()));
|
||||
|
||||
FileEnvelopeModel fileEnvelope = repository.exportDescription(descriptionFileTransformerModel, format);
|
||||
|
|
|
@ -26,7 +26,7 @@ public class VisibilityServiceImpl implements VisibilityService {
|
|||
}
|
||||
|
||||
private void initRules(){
|
||||
if (this.rulesBySources == null) return;
|
||||
if (this.rulesBySources != null) return;
|
||||
rulesBySources = new HashMap<>();
|
||||
for (FieldEntity fieldEntity : this.definition.getAllField()){
|
||||
if (fieldEntity.getVisibilityRules() != null && !fieldEntity.getVisibilityRules().isEmpty()) {
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
package eu.eudat.controllers;
|
||||
|
||||
import eu.eudat.audit.AuditableAction;
|
||||
import eu.eudat.file.transformer.models.misc.FileFormat;
|
||||
import eu.eudat.model.file.ExportRequestModel;
|
||||
import eu.eudat.model.file.FileEnvelope;
|
||||
import eu.eudat.model.file.RepositoryFileFormat;
|
||||
import eu.eudat.service.transformer.FileTransformerService;
|
||||
import gr.cite.tools.auditing.AuditService;
|
||||
import gr.cite.tools.logging.LoggerService;
|
||||
|
@ -37,10 +37,10 @@ public class FileTransformerController {
|
|||
}
|
||||
|
||||
@GetMapping("/available")
|
||||
public List<FileFormat> getAvailableConfigurations() {
|
||||
public List<RepositoryFileFormat> getAvailableConfigurations() {
|
||||
logger.debug(new MapLogEntry("getAvailableConfigurations"));
|
||||
|
||||
List<FileFormat> model = this.fileTransformerService.getAvailableExportFileFormats();
|
||||
List<RepositoryFileFormat> model = this.fileTransformerService.getAvailableExportFileFormats();
|
||||
this.auditService.track(AuditableAction.FileTransformer_GetAvailableConfigurations);
|
||||
//this.auditService.trackIdentity(AuditableAction.IdentityTracking_Action);
|
||||
|
||||
|
@ -60,11 +60,11 @@ public class FileTransformerController {
|
|||
}
|
||||
|
||||
@PostMapping("/export-description")
|
||||
public ResponseEntity<byte[]> exportDescription(@RequestBody ExportRequestModel requestModel) throws InvalidApplicationException, IOException {
|
||||
public ResponseEntity<byte[]> exportDescription(@RequestBody ExportRequestModel requestModel) {
|
||||
logger.debug(new MapLogEntry("exporting dmp"));
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
|
||||
FileEnvelope fileEnvelope = this.fileTransformerService.exportDescription(requestModel.getId(), requestModel.getFormat());
|
||||
FileEnvelope fileEnvelope = this.fileTransformerService.exportDescription(requestModel.getId(), requestModel.getRepositoryId(), requestModel.getFormat());
|
||||
headers.add("Content-Disposition", "attachment;filename=" + fileEnvelope.getFilename());
|
||||
byte[] data = fileEnvelope.getFile();
|
||||
headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
|
||||
|
|
|
@ -4,7 +4,7 @@ deposit:
|
|||
repositoryId: zenodo
|
||||
pdfTransformerId: docx-file-transformer
|
||||
rdaTransformerId: rda-file-transformer
|
||||
issuer-url: ${ZENODO_ISSUER_URI:IDP_APIKEY_ISSUER_URI}/protocol/openid-connect/token
|
||||
client-id: ${ZENODO_DEPOSIT_CLIENT_ID:}
|
||||
client-secret: ${ZENODO_DEPOSIT_CLIENT_SECRET:}
|
||||
scope: ${ZENODO_DEPOSIT_SCOPE:}
|
||||
issuer-url: ${IDP_ISSUER_URI_TOKEN}
|
||||
client-id: ${ZENODO_DEPOSIT_CLIENT_ID}
|
||||
client-secret: ${ZENODO_DEPOSIT_CLIENT_SECRET}
|
||||
scope: ${ZENODO_DEPOSIT_SCOPE}
|
|
@ -3,21 +3,14 @@ transformer:
|
|||
- url: http://dev04.local.cite.gr:55330/file/docx
|
||||
transformerId: docx-file-transformer
|
||||
codes: [ docx, pdf ]
|
||||
issuer-url: ${IDP_ISSUER_URI_TOKEN:}
|
||||
client-id: ${IDP_APIKEY_CLIENT_ID:}
|
||||
client-secret: ${IDP_APIKEY_CLIENT_SECRET:}
|
||||
scope: ${IDP_APIKEY_SCOPE:}
|
||||
issuer-url: ${IDP_ISSUER_URI_TOKEN}
|
||||
client-id: ${IDP_APIKEY_CLIENT_ID}
|
||||
client-secret: ${IDP_APIKEY_CLIENT_SECRET}
|
||||
scope: ${IDP_APIKEY_SCOPE}
|
||||
- url: http://dev04.local.cite.gr:55330/file/rdajson
|
||||
transformerId: rda-file-transformer
|
||||
codes: [ json ]
|
||||
issuer-url: ${IDP_ISSUER_URI_TOKEN:}
|
||||
client-id: ${IDP_APIKEY_CLIENT_ID:}
|
||||
client-secret: ${IDP_APIKEY_CLIENT_SECRET:}
|
||||
scope: ${IDP_APIKEY_SCOPE:}
|
||||
- url: http://localhost:8087
|
||||
transformerId: import-export-file-transformer
|
||||
codes: [ xml ]
|
||||
issuer-url: ${IDP_ISSUER_URI_TOKEN:}
|
||||
client-id: ${IDP_APIKEY_CLIENT_ID:}
|
||||
client-secret: ${IDP_APIKEY_CLIENT_SECRET:}
|
||||
scope: ${IDP_APIKEY_SCOPE:}
|
||||
issuer-url: ${IDP_ISSUER_URI_TOKEN}
|
||||
client-id: ${IDP_APIKEY_CLIENT_ID}
|
||||
client-secret: ${IDP_APIKEY_CLIENT_SECRET}
|
||||
scope: ${IDP_APIKEY_SCOPE}
|
|
@ -1,5 +1,6 @@
|
|||
export interface FileFormat {
|
||||
export interface RepositoryFileFormat {
|
||||
format: string;
|
||||
hasLogo: boolean;
|
||||
icon: string;
|
||||
repositoryId: string;
|
||||
}
|
||||
|
|
|
@ -13,7 +13,6 @@ import { catchError, map } from 'rxjs/operators';
|
|||
import { nameof } from 'ts-simple-nameof';
|
||||
import { ConfigurationService } from '../configuration/configuration.service';
|
||||
import { BaseHttpV2Service } from '../http/base-http-v2.service';
|
||||
import { FileFormat } from '@app/core/model/file/file-format.model';
|
||||
import { BaseHttpParams } from '@common/http/base-http-params';
|
||||
import { InterceptorType } from '@common/http/interceptors/interceptor-type';
|
||||
import { DescriptionValidationResult } from '@app/ui/dmp/dmp-finalize-dialog/dmp-finalize-dialog.component';
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
import { HttpHeaders } from '@angular/common/http';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { FileFormat } from '@app/core/model/file/file-format.model';
|
||||
import { BaseService } from '@common/base/base.service';
|
||||
import { Guid } from '@common/types/guid';
|
||||
import { Observable, throwError } from 'rxjs';
|
||||
import { catchError } from 'rxjs/operators';
|
||||
import { ConfigurationService } from '../configuration/configuration.service';
|
||||
import { BaseHttpV2Service } from '../http/base-http-v2.service';
|
||||
import { RepositoryFileFormat } from '@app/core/model/file/file-format.model';
|
||||
|
||||
@Injectable()
|
||||
export class FileTransformerHttpService extends BaseService {
|
||||
|
@ -20,20 +20,20 @@ export class FileTransformerHttpService extends BaseService {
|
|||
|
||||
private get apiBase(): string { return `${this.configurationService.server}file-transformer`; }
|
||||
|
||||
getAvailableConfigurations(): Observable<FileFormat[]> {
|
||||
getAvailableConfigurations(): Observable<RepositoryFileFormat[]> {
|
||||
const url = `${this.apiBase}/available`;
|
||||
return this.http.get<FileFormat[]>(url).pipe(catchError((error: any) => throwError(error)));
|
||||
return this.http.get<RepositoryFileFormat[]>(url).pipe(catchError((error: any) => throwError(error)));
|
||||
}
|
||||
|
||||
exportDmp(dmpId: Guid, format: string): Observable<any> {
|
||||
exportDmp(dmpId: Guid, repositoryId: string, format: string): Observable<any> {
|
||||
//TODO: implement
|
||||
const url = `${this.apiBase}/export-dmp`;
|
||||
return this.http.post<any>(url, {id: dmpId, format: format}, {responseType: 'blob', observe: 'response'}).pipe(catchError((error: any) => throwError(error)));
|
||||
return this.http.post<any>(url, {id: dmpId, repositoryId: repositoryId, format: format}, {responseType: 'blob', observe: 'response'}).pipe(catchError((error: any) => throwError(error)));
|
||||
}
|
||||
|
||||
exportDescription(id: Guid, format: string): Observable<any> {
|
||||
exportDescription(id: Guid, repositoryId: string, format: string): Observable<any> {
|
||||
//TODO: implement
|
||||
const url = `${this.apiBase}/export-description`;
|
||||
return this.http.post<any>(url, {id: id, format: format}, {responseType: 'blob', observe: 'response'}).pipe(catchError((error: any) => throwError(error)));
|
||||
return this.http.post<any>(url, {id: id, repositoryId: repositoryId, format: format}, {responseType: 'blob', observe: 'response'}).pipe(catchError((error: any) => throwError(error)));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import { Injectable } from '@angular/core';
|
||||
import { FileFormat } from '@app/core/model/file/file-format.model';
|
||||
import { BaseService } from '@common/base/base.service';
|
||||
import { catchError, takeUntil } from 'rxjs/operators';
|
||||
import { FileTransformerHttpService } from './file-transformer.http.service';
|
||||
|
@ -8,6 +7,7 @@ import * as FileSaver from 'file-saver';
|
|||
import { MatomoService } from '../matomo/matomo-service';
|
||||
import { FileUtils } from '../utilities/file-utils.service';
|
||||
import { AuthService } from '../auth/auth.service';
|
||||
import { RepositoryFileFormat } from '@app/core/model/file/file-format.model';
|
||||
|
||||
@Injectable()
|
||||
export class FileTransformerService extends BaseService {
|
||||
|
@ -22,8 +22,8 @@ export class FileTransformerService extends BaseService {
|
|||
private _initialized: boolean = false;
|
||||
private _loading: boolean = false;
|
||||
|
||||
private _availableFormats: FileFormat[] = [];
|
||||
get availableFormats(): FileFormat[] {
|
||||
private _availableFormats: RepositoryFileFormat[] = [];
|
||||
get availableFormats(): RepositoryFileFormat[] {
|
||||
if (!this.authentication.currentAccountIsAuthenticated()){
|
||||
return;
|
||||
}
|
||||
|
@ -46,9 +46,9 @@ export class FileTransformerService extends BaseService {
|
|||
});
|
||||
}
|
||||
|
||||
exportDmp(id: Guid, format: string) {
|
||||
exportDmp(id: Guid, repositoryId: string, format: string) {
|
||||
this._loading = true;
|
||||
this.fileTransformerHttpService.exportDmp(id, format).pipe(takeUntil(this._destroyed), catchError((error) => {
|
||||
this.fileTransformerHttpService.exportDmp(id, repositoryId, format).pipe(takeUntil(this._destroyed), catchError((error) => {
|
||||
this._loading = false;
|
||||
return null;
|
||||
})).subscribe(result => {
|
||||
|
@ -62,9 +62,9 @@ export class FileTransformerService extends BaseService {
|
|||
});
|
||||
}
|
||||
|
||||
exportDescription(id: Guid, format: string) {
|
||||
exportDescription(id: Guid, repositoryId: string, format: string) {
|
||||
this._loading = true;
|
||||
this.fileTransformerHttpService.exportDescription(id, format).pipe(takeUntil(this._destroyed), catchError((error) => {
|
||||
this.fileTransformerHttpService.exportDescription(id, repositoryId, format).pipe(takeUntil(this._destroyed), catchError((error) => {
|
||||
this._loading = false;
|
||||
return null;
|
||||
})).subscribe(result => {
|
||||
|
|
|
@ -8,7 +8,6 @@ import { DescriptionTemplate } from '@app/core/model/description-template/descri
|
|||
import { Description } from '@app/core/model/description/description';
|
||||
import { Dmp, DmpUser } from "@app/core/model/dmp/dmp";
|
||||
import { DmpReference } from '@app/core/model/dmp/dmp-reference';
|
||||
import { FileFormat } from '@app/core/model/file/file-format.model';
|
||||
import { Reference } from '@app/core/model/reference/reference';
|
||||
import { RecentActivityItemLookup } from '@app/core/query/recent-activity-item-lookup.lookup';
|
||||
import { DashboardService } from "@app/core/services/dashboard/dashboard.service";
|
||||
|
|
|
@ -11,7 +11,6 @@ import { DescriptionTemplate } from '@app/core/model/description-template/descri
|
|||
import { Description } from '@app/core/model/description/description';
|
||||
import { Dmp, DmpUser } from '@app/core/model/dmp/dmp';
|
||||
import { DmpReference } from '@app/core/model/dmp/dmp-reference';
|
||||
import { FileFormat } from '@app/core/model/file/file-format.model';
|
||||
import { ReferenceType } from '@app/core/model/reference-type/reference-type';
|
||||
import { Reference } from '@app/core/model/reference/reference';
|
||||
import { RecentActivityItemLookup } from '@app/core/query/recent-activity-item-lookup.lookup';
|
||||
|
|
|
@ -10,7 +10,6 @@ import { RecentActivityItem } from '@app/core/model/dashboard/recent-activity-it
|
|||
import { Description } from '@app/core/model/description/description';
|
||||
import { Dmp, DmpUser } from '@app/core/model/dmp/dmp';
|
||||
import { DmpReference } from '@app/core/model/dmp/dmp-reference';
|
||||
import { FileFormat } from '@app/core/model/file/file-format.model';
|
||||
import { ReferenceType } from '@app/core/model/reference-type/reference-type';
|
||||
import { Reference } from '@app/core/model/reference/reference';
|
||||
import { RecentActivityItemLookup } from '@app/core/query/recent-activity-item-lookup.lookup';
|
||||
|
|
|
@ -14,7 +14,6 @@ import { DescriptionTemplate } from '@app/core/model/description-template/descri
|
|||
import { Description } from '@app/core/model/description/description';
|
||||
import { Dmp, DmpDescriptionTemplate, DmpUser } from '@app/core/model/dmp/dmp';
|
||||
import { DmpReference } from '@app/core/model/dmp/dmp-reference';
|
||||
import { FileFormat } from '@app/core/model/file/file-format.model';
|
||||
import { ReferenceType } from '@app/core/model/reference-type/reference-type';
|
||||
import { Reference } from '@app/core/model/reference/reference';
|
||||
import { DescriptionLookup } from '@app/core/query/description.lookup';
|
||||
|
|
|
@ -38,7 +38,7 @@
|
|||
</button>
|
||||
</mat-menu>
|
||||
<mat-menu #exportMenu="matMenu" xPosition="before">
|
||||
<button mat-menu-item *ngFor='let fileTransformer of fileTransformerService.availableFormats' (click)="fileTransformerService.exportDescription(description.id, fileTransformer.format)">
|
||||
<button mat-menu-item *ngFor='let fileTransformer of fileTransformerService.availableFormats' (click)="fileTransformerService.exportDescription(description.id, fileTransformer.repositoryId, fileTransformer.format)">
|
||||
<i class="fa pr-2" [ngClass]="fileTransformer.icon ? fileTransformer.icon : 'fa-file-o'"></i>
|
||||
<span>{{'GENERAL.FILE-TRANSFORMER.' + fileTransformer.format.toUpperCase() | translate}}</span>
|
||||
</button>
|
||||
|
|
|
@ -117,7 +117,7 @@
|
|||
{{ 'DESCRIPTION-OVERVIEW.ACTIONS.EXPORT' | translate }}</p>
|
||||
</div>
|
||||
<mat-menu #exportMenu="matMenu" xPosition="before">
|
||||
<button mat-menu-item *ngFor='let fileTransformer of fileTransformerService.availableFormats' (click)="fileTransformerService.exportDescription(description.id, fileTransformer.format)">
|
||||
<button mat-menu-item *ngFor='let fileTransformer of fileTransformerService.availableFormats' (click)="fileTransformerService.exportDescription(description.id, fileTransformer.repositoryId, fileTransformer.format)">
|
||||
<i class="fa pr-2" [ngClass]="fileTransformer.icon ? fileTransformer.icon : 'fa-file-o'"></i>
|
||||
<span>{{'GENERAL.FILE-TRANSFORMER.' + fileTransformer.format.toUpperCase() | translate}}</span>
|
||||
</button>
|
||||
|
|
|
@ -38,7 +38,7 @@
|
|||
<a class="col-auto pointer" *ngIf="isAuthenticated()" [matMenuTriggerFor]="actionsMenu"><span class="material-icons icon-align pl-2">more_horiz</span></a>
|
||||
</div>
|
||||
<mat-menu #exportMenu="matMenu" xPosition="before">
|
||||
<button mat-menu-item *ngFor='let fileTransformer of fileTransformerService.availableFormats' (click)="fileTransformerService.exportDmp(dmp.id, fileTransformer.format)">
|
||||
<button mat-menu-item *ngFor='let fileTransformer of fileTransformerService.availableFormats' (click)="fileTransformerService.exportDmp(dmp.id, fileTransformer.repositoryId, fileTransformer.format)">
|
||||
<i class="fa pr-2" [ngClass]="fileTransformer.hasLogo ? fileTransformer.icon : 'fa-file-o'"></i>
|
||||
<span>{{'GENERAL.FILE-TRANSFORMER.' + fileTransformer.format.toUpperCase() | translate}}</span>
|
||||
</button>
|
||||
|
|
|
@ -166,9 +166,9 @@
|
|||
</p>
|
||||
</div>
|
||||
<mat-menu #exportMenu="matMenu" xPosition="before">
|
||||
<button mat-menu-item *ngFor='let fileTransformer of fileTransformerService.availableFormats' (click)="fileTransformerService.exportDmp(dmp.id, fileTransformer.format)">
|
||||
<button mat-menu-item *ngFor='let fileTransformer of fileTransformerService.availableFormats' (click)="fileTransformerService.exportDmp(dmp.id, fileTransformer.repositoryId, fileTransformer.format)">
|
||||
<i class="fa pr-2" [ngClass]="fileTransformer.icon ? fileTransformer.icon : 'fa-file-o'"></i>
|
||||
<span>{{'GENERAL.FILE-TRANSFORMER.' + fileTransformer?.formatName?.toUpperCase() | translate}}</span>
|
||||
<span>{{'GENERAL.FILE-TRANSFORMER.' + fileTransformer?.format?.toUpperCase() | translate}}</span>
|
||||
</button>
|
||||
</mat-menu>
|
||||
</div>
|
||||
|
|
|
@ -175,7 +175,15 @@ export class DmpOverviewComponent extends BaseComponent implements OnInit {
|
|||
}
|
||||
});
|
||||
if (this.isAuthenticated()) {
|
||||
this.depositRepositoriesService.getAvailableRepos()
|
||||
this.depositRepositoriesService.getAvailableRepos([
|
||||
nameof<DepositConfiguration>(x => x.depositType),
|
||||
nameof<DepositConfiguration>(x => x.repositoryId),
|
||||
nameof<DepositConfiguration>(x => x.repositoryAuthorizationUrl),
|
||||
nameof<DepositConfiguration>(x => x.repositoryRecordUrl),
|
||||
nameof<DepositConfiguration>(x => x.repositoryClientId),
|
||||
nameof<DepositConfiguration>(x => x.hasLogo),
|
||||
nameof<DepositConfiguration>(x => x.redirectUri)
|
||||
])
|
||||
.pipe(takeUntil(this._destroyed))
|
||||
.subscribe(
|
||||
repos => {
|
||||
|
|
Loading…
Reference in New Issue