diff --git a/dmp-backend/web/src/main/java/eu/eudat/cache/ResponsesCache.java b/dmp-backend/web/src/main/java/eu/eudat/cache/ResponsesCache.java index 4a5858bde..0a09a6731 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/cache/ResponsesCache.java +++ b/dmp-backend/web/src/main/java/eu/eudat/cache/ResponsesCache.java @@ -41,6 +41,7 @@ public class ResponsesCache { caches.add(new GuavaCache("researchers", CacheBuilder.newBuilder().expireAfterAccess(HOW_MANY, TIME_UNIT).build())); caches.add(new GuavaCache("externalDatasets", CacheBuilder.newBuilder().expireAfterAccess(HOW_MANY, TIME_UNIT).build())); caches.add(new GuavaCache("currencies", CacheBuilder.newBuilder().expireAfterAccess(HOW_MANY, TIME_UNIT).build())); + caches.add(new GuavaCache("licenses", CacheBuilder.newBuilder().expireAfterAccess(HOW_MANY, TIME_UNIT).build())); simpleCacheManager.setCaches(caches); logger.info("OK"); return simpleCacheManager; diff --git a/dmp-backend/web/src/main/java/eu/eudat/controllers/Licenses.java b/dmp-backend/web/src/main/java/eu/eudat/controllers/Licenses.java new file mode 100644 index 000000000..ee251c2f4 --- /dev/null +++ b/dmp-backend/web/src/main/java/eu/eudat/controllers/Licenses.java @@ -0,0 +1,41 @@ +package eu.eudat.controllers; + +import eu.eudat.logic.managers.LicenseManager; +import eu.eudat.logic.proxy.config.exceptions.HugeResultSet; +import eu.eudat.logic.proxy.config.exceptions.NoURLFound; +import eu.eudat.logic.services.ApiContext; +import eu.eudat.models.data.helpers.responses.ResponseItem; +import eu.eudat.models.data.license.LicenseModel; +import eu.eudat.models.data.security.Principal; +import eu.eudat.types.ApiMessageCode; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + + +@RestController +@CrossOrigin +@RequestMapping(value = {"/api/external/licenses"}) +public class Licenses extends BaseController { + + private LicenseManager licenseManager; + + @Autowired + public Licenses(ApiContext apiContext, LicenseManager licenseManager) { + super(apiContext); + this.licenseManager = licenseManager; + } + + @RequestMapping(method = RequestMethod.GET, produces = "application/json") + public @ResponseBody + ResponseEntity>> listExternalLicenses( + @RequestParam(value = "query", required = false) String query, @RequestParam(value = "type", required = false) String type, Principal principal + ) throws HugeResultSet, NoURLFound { + List licenseModels = this.licenseManager.getLicenses(query, type); + return ResponseEntity.status(HttpStatus.OK).body(new ResponseItem>().status(ApiMessageCode.NO_MESSAGE).payload(licenseModels)); + } +} + diff --git a/dmp-backend/web/src/main/java/eu/eudat/logic/managers/DataManagementPlanManager.java b/dmp-backend/web/src/main/java/eu/eudat/logic/managers/DataManagementPlanManager.java index acb51508e..017f84eb3 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/logic/managers/DataManagementPlanManager.java +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/managers/DataManagementPlanManager.java @@ -1,6 +1,5 @@ package eu.eudat.logic.managers; -import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import eu.eudat.configurations.dynamicgrant.DynamicGrantConfiguration; @@ -14,10 +13,8 @@ import eu.eudat.data.enumeration.notification.ActiveStatus; import eu.eudat.data.enumeration.notification.ContactType; import eu.eudat.data.enumeration.notification.NotificationType; import eu.eudat.data.enumeration.notification.NotifyState; -import eu.eudat.data.query.items.item.dmp.DataManagementPlanCriteriaRequest; import eu.eudat.data.query.items.table.datasetprofile.DatasetProfileTableRequestItem; import eu.eudat.data.query.items.table.dmp.DataManagementPlanTableRequest; -import eu.eudat.data.query.items.table.dmp.DataManagmentPlanPublicTableRequest; import eu.eudat.elastic.criteria.DmpCriteria; import eu.eudat.elastic.entities.Collaborator; import eu.eudat.elastic.entities.Dmp; @@ -83,8 +80,6 @@ import org.springframework.web.multipart.MultipartFile; import org.w3c.dom.Document; import org.w3c.dom.Element; -import javax.persistence.criteria.Join; -import javax.persistence.criteria.JoinType; import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBException; import javax.xml.bind.Unmarshaller; @@ -1650,6 +1645,7 @@ public class DataManagementPlanManager { headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON)); headers.setContentType(MediaType.APPLICATION_JSON); String createData = null; + Map extraProperties = dmp.getExtraProperties() != null ? new org.json.JSONObject(dmp.getExtraProperties()).toMap() : new HashMap<>(); StringBuilder dataBuilder = new StringBuilder(); dataBuilder.append("{\n \"metadata\": {\n"); dataBuilder.append( " \"title\": \"").append(dmp.getLabel()).append("\",\n"); @@ -1663,6 +1659,9 @@ public class DataManagementPlanManager { dataBuilder.append(" \"related_identifiers\": [{\n"); dataBuilder.append(" \t\t\"identifier\": \"").append((this.environment.getProperty("dmp.domain") + "/external/zenodo/" + id.toString())).append("\",\n"); dataBuilder.append(" \t\t\"relation\": \"isIdenticalTo\"}],\n"); + if (extraProperties.get("license") != null) { + dataBuilder.append(" \"license\": \"").append(((Map)extraProperties.get("license")).get("pid")).append("\",\n"); + } } else { dataBuilder.append("restricted\",\n"); dataBuilder.append(" \"access_conditions\": \"\",\n"); diff --git a/dmp-backend/web/src/main/java/eu/eudat/logic/managers/LicenseManager.java b/dmp-backend/web/src/main/java/eu/eudat/logic/managers/LicenseManager.java new file mode 100644 index 000000000..da06aedd3 --- /dev/null +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/managers/LicenseManager.java @@ -0,0 +1,48 @@ +package eu.eudat.logic.managers; + +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import eu.eudat.data.dao.criteria.DataRepositoryCriteria; +import eu.eudat.data.entities.DataRepository; +import eu.eudat.logic.proxy.config.ExternalUrlCriteria; +import eu.eudat.logic.proxy.config.exceptions.HugeResultSet; +import eu.eudat.logic.proxy.config.exceptions.NoURLFound; +import eu.eudat.logic.services.ApiContext; +import eu.eudat.models.data.datarepository.DataRepositoryModel; +import eu.eudat.models.data.license.LicenseModel; +import eu.eudat.models.data.security.Principal; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * Created by ikalyvas on 9/3/2018. + */ +@Component +public class LicenseManager { + private ApiContext apiContext; + + @Autowired + public LicenseManager(ApiContext apiContext) { + this.apiContext = apiContext; + } + + public List getLicenses(String query, String type) throws HugeResultSet, NoURLFound { + ExternalUrlCriteria externalUrlCriteria = new ExternalUrlCriteria(query); + List> remoteRepos = this.apiContext.getOperationsContext().getRemoteFetcher().getlicenses(externalUrlCriteria, type); + + DataRepositoryCriteria criteria = new DataRepositoryCriteria(); + if (!query.isEmpty()) criteria.setLike(query); + + List licenseModels = new LinkedList<>(); + + ObjectMapper mapper = new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + licenseModels.addAll(remoteRepos.stream().map(item -> mapper.convertValue(item, LicenseModel.class)).collect(Collectors.toList())); + licenseModels = licenseModels.stream().filter(licenseModel -> licenseModel.getName().contains(query)).collect(Collectors.toList()); + return licenseModels; + } +} diff --git a/dmp-backend/web/src/main/java/eu/eudat/logic/proxy/config/ExternalUrls.java b/dmp-backend/web/src/main/java/eu/eudat/logic/proxy/config/ExternalUrls.java index f7e38ba20..cb47186c1 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/logic/proxy/config/ExternalUrls.java +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/proxy/config/ExternalUrls.java @@ -24,6 +24,7 @@ public class ExternalUrls implements Serializable { DatasetUrls datasets; /*TagUrls tags;*/ FunderUrls funders; + LicenseUrls licenses; public RegistryUrls getRegistries() { @@ -133,6 +134,15 @@ public class ExternalUrls implements Serializable { public void setDatasets(DatasetUrls datasets) { this.datasets = datasets; } + + public LicenseUrls getLicenses() { + return licenses; + } + + @XmlElement(name = "licenses") + public void setLicenses(LicenseUrls licenses) { + this.licenses = licenses; + } } diff --git a/dmp-backend/web/src/main/java/eu/eudat/logic/proxy/config/entities/LicenseUrls.java b/dmp-backend/web/src/main/java/eu/eudat/logic/proxy/config/entities/LicenseUrls.java new file mode 100644 index 000000000..79e1e1b47 --- /dev/null +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/proxy/config/entities/LicenseUrls.java @@ -0,0 +1,33 @@ +package eu.eudat.logic.proxy.config.entities; + +import eu.eudat.logic.proxy.config.FetchStrategy; +import eu.eudat.logic.proxy.config.UrlConfiguration; + +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlElementWrapper; +import java.util.List; + + +public class LicenseUrls { + List urls; + FetchStrategy fetchMode; + + public List getUrls() { + return urls; + } + + @XmlElementWrapper + @XmlElement(name = "urlConfig") + public void setUrls(List urls) { + this.urls = urls; + } + + public FetchStrategy getFetchMode() { + return fetchMode; + } + + @XmlElement(name = "fetchMode") + public void setFetchMode(FetchStrategy fetchMode) { + this.fetchMode = fetchMode; + } +} diff --git a/dmp-backend/web/src/main/java/eu/eudat/logic/proxy/fetching/RemoteFetcher.java b/dmp-backend/web/src/main/java/eu/eudat/logic/proxy/fetching/RemoteFetcher.java index cbaf4dea1..41f42281c 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/logic/proxy/fetching/RemoteFetcher.java +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/proxy/fetching/RemoteFetcher.java @@ -126,6 +126,15 @@ public class RemoteFetcher { return getAll(urlConfigs, fetchStrategy, externalUrlCriteria); } + @Cacheable("licenses") + public List> getlicenses(ExternalUrlCriteria externalUrlCriteria, String key) throws NoURLFound, HugeResultSet { + List urlConfigs = + key != null && !key.isEmpty() ? configLoader.getExternalUrls().getLicenses().getUrls().stream().filter(item -> item.getKey().equals(key)).collect(Collectors.toList()) + : configLoader.getExternalUrls().getLicenses().getUrls(); + FetchStrategy fetchStrategy = configLoader.getExternalUrls().getLicenses().getFetchMode(); + return getAll(urlConfigs, fetchStrategy, externalUrlCriteria); + } + private List> getAll(List urlConfigs, FetchStrategy fetchStrategy, ExternalUrlCriteria externalUrlCriteria) throws NoURLFound, HugeResultSet { diff --git a/dmp-backend/web/src/main/java/eu/eudat/models/data/external/LicensesExternalSourcesModel.java b/dmp-backend/web/src/main/java/eu/eudat/models/data/external/LicensesExternalSourcesModel.java new file mode 100644 index 000000000..0f8d648ac --- /dev/null +++ b/dmp-backend/web/src/main/java/eu/eudat/models/data/external/LicensesExternalSourcesModel.java @@ -0,0 +1,19 @@ +package eu.eudat.models.data.external; + +import java.util.List; +import java.util.Map; + + +public class LicensesExternalSourcesModel extends ExternalListingItem { + @Override + public LicensesExternalSourcesModel fromExternalItem(List> values) { + for (Map item : values) { + ExternalSourcesItemModel model = new ExternalSourcesItemModel(); + model.setId(item.get("id")); + model.setUri(item.get("uri")); + model.setName(item.get("name")); + this.add(model); + } + return this; + } +} diff --git a/dmp-backend/web/src/main/java/eu/eudat/models/data/license/LicenseModel.java b/dmp-backend/web/src/main/java/eu/eudat/models/data/license/LicenseModel.java new file mode 100644 index 000000000..d6ea7e67e --- /dev/null +++ b/dmp-backend/web/src/main/java/eu/eudat/models/data/license/LicenseModel.java @@ -0,0 +1,127 @@ +package eu.eudat.models.data.license; + +import eu.eudat.data.entities.DataRepository; +import eu.eudat.data.entities.UserInfo; +import eu.eudat.models.DataModel; + +import java.util.Date; +import java.util.UUID; + +public class LicenseModel implements DataModel { + private UUID id; + private String name; + private String pid; + private String abbreviation; + private String uri; + private Date created; + private Date modified; + private String tag; // Api fetching the data + private String source; // Actual harvested source + + public UUID getId() { + return id; + } + public void setId(UUID id) { + this.id = id; + } + + public String getName() { + return name; + } + public void setName(String name) { + this.name = name; + } + + public String getPid() { + return pid; + } + public void setPid(String pid) { + this.pid = pid; + } + + public String getAbbreviation() { + return abbreviation; + } + public void setAbbreviation(String abbreviation) { + this.abbreviation = abbreviation; + } + + public String getUri() { + return uri; + } + public void setUri(String uri) { + this.uri = uri; + } + + public Date getCreated() { + return created; + } + public void setCreated(Date created) { + this.created = created; + } + + public Date getModified() { + return modified; + } + public void setModified(Date modified) { + this.modified = modified; + } + + public String getTag() { + return tag; + } + public void setTag(String tag) { + this.tag = tag; + } + + public String getSource() { + return source; + } + public void setSource(String source) { + this.source = source; + } + + @Override + public LicenseModel fromDataModel(DataRepository entity) { + this.setAbbreviation(entity.getAbbreviation()); + this.setName(entity.getLabel()); + this.setUri(entity.getUri()); + this.setId(entity.getId()); + this.setPid(entity.getReference()); + String source1 = entity.getReference().substring(0, entity.getReference().indexOf(":")); + if (source1.equals("dmp")) { + this.source = "Internal"; + } else { + this.source = source1; + } + return this; + } + + @Override + public DataRepository toDataModel() throws Exception { + DataRepository dataRepository = new DataRepository(); + dataRepository.setId(this.id != null ? this.id : UUID.randomUUID()); + dataRepository.setAbbreviation(this.abbreviation); + dataRepository.setCreated(this.created != null ? this.created : new Date()); + dataRepository.setModified(new Date()); + dataRepository.setLabel(this.name); + if (this.source != null) { + if (this.source.equals("Internal") || this.source.equals(this.id.toString().substring(0, this.source.length()))) { + dataRepository.setReference(this.id.toString()); + } else { + dataRepository.setReference(this.source + ":" + dataRepository.getId()); + } + } else { + dataRepository.setReference("dmp:" + dataRepository.getId()); + } + dataRepository.setUri(this.uri); + dataRepository.setStatus((short) 0); + dataRepository.setCreationUser(new UserInfo()); + return dataRepository; + } + + @Override + public String getHint() { + return null; + } +} diff --git a/dmp-backend/web/src/main/resources/externalUrls/ExternalUrls.xml b/dmp-backend/web/src/main/resources/externalUrls/ExternalUrls.xml index db6c40d74..498f492f1 100644 --- a/dmp-backend/web/src/main/resources/externalUrls/ExternalUrls.xml +++ b/dmp-backend/web/src/main/resources/externalUrls/ExternalUrls.xml @@ -937,6 +937,30 @@ FIRST + + + + opendefinition + + 1 + External + https://licenses.opendefinition.org/licenses/groups/all.json + 1 + application/vnd.api+json; charset=utf-8 + + $[*] + + 'id' + 'title' + 'url' + 'maintainer' + + + + + FIRST + + diff --git a/dmp-frontend/src/app/core/query/license/license-criteria.ts b/dmp-frontend/src/app/core/query/license/license-criteria.ts new file mode 100644 index 000000000..ea2c3138a --- /dev/null +++ b/dmp-frontend/src/app/core/query/license/license-criteria.ts @@ -0,0 +1,5 @@ +import { BaseCriteria } from "../base-criteria"; + +export class LicenseCriteria extends BaseCriteria { + public type: string; +} diff --git a/dmp-frontend/src/app/core/services/external-sources/external-sources.service.ts b/dmp-frontend/src/app/core/services/external-sources/external-sources.service.ts index 471b6f954..3e91ec516 100644 --- a/dmp-frontend/src/app/core/services/external-sources/external-sources.service.ts +++ b/dmp-frontend/src/app/core/services/external-sources/external-sources.service.ts @@ -12,6 +12,7 @@ import { ServiceCriteria } from '../../query/service/service-criteria'; import { TagCriteria } from '../../query/tag/tag-criteria'; import { BaseHttpService } from '../http/base-http.service'; import { ConfigurationService } from '../configuration/configuration.service'; +import { LicenseCriteria } from '@app/core/query/license/license-criteria'; @Injectable() export class ExternalSourcesService { @@ -40,6 +41,10 @@ export class ExternalSourcesService { return this.http.get(this.actionUrl + 'services' + '?query=' + requestItem.criteria.like + '&type=' + requestItem.criteria.type, { headers: this.headers }); } + public searchLicense(requestItem: RequestItem): Observable { + return this.http.get(this.actionUrl + 'licenses' + '?query=' + requestItem.criteria.like + '&type=' + requestItem.criteria.type, { headers: this.headers }); + } + public searchDatasetTags(requestItem: RequestItem): Observable { // return Observable.of([ // { id: '1', name: 'Tag 1', description: '' }, diff --git a/dmp-frontend/src/app/ui/dmp/editor/general-tab/extra-properties-form.model.ts b/dmp-frontend/src/app/ui/dmp/editor/general-tab/extra-properties-form.model.ts index 5ac10bbf5..ea0a76547 100644 --- a/dmp-frontend/src/app/ui/dmp/editor/general-tab/extra-properties-form.model.ts +++ b/dmp-frontend/src/app/ui/dmp/editor/general-tab/extra-properties-form.model.ts @@ -4,9 +4,11 @@ import { BackendErrorValidator } from '@common/forms/validation/custom-validator export class ExtraPropertiesFormModel { public language: string; + public license: string; fromModel(item: any): ExtraPropertiesFormModel { this.language = item.language; + this.license = item.license; return this; } @@ -14,7 +16,8 @@ export class ExtraPropertiesFormModel { if (context == null) { context = this.createValidationContext(); } const formGroup = new FormBuilder().group({ - language: [{ value: this.language, disabled: disabled }, context.getValidation('language').validators] + language: [{ value: this.language, disabled: disabled }, context.getValidation('language').validators], + license: [{ value: this.license, disabled: disabled }, context.getValidation('license').validators] }); return formGroup; } @@ -22,6 +25,7 @@ export class ExtraPropertiesFormModel { createValidationContext(): ValidationContext { const baseContext: ValidationContext = new ValidationContext(); baseContext.validation.push({ key: 'language', validators: [] }); + baseContext.validation.push({ key: 'license', validators: [] }); return baseContext; } diff --git a/dmp-frontend/src/app/ui/dmp/editor/general-tab/general-tab.component.html b/dmp-frontend/src/app/ui/dmp/editor/general-tab/general-tab.component.html index b7a6386e2..bda087a2e 100644 --- a/dmp-frontend/src/app/ui/dmp/editor/general-tab/general-tab.component.html +++ b/dmp-frontend/src/app/ui/dmp/editor/general-tab/general-tab.component.html @@ -81,6 +81,19 @@ +
+ + + + + + {{formGroup.get('extraProperties').get('language').getError('backendError').message}} + + {{'GENERAL.VALIDATION.REQUIRED' | translate}} + + +
diff --git a/dmp-frontend/src/app/ui/dmp/editor/general-tab/general-tab.component.ts b/dmp-frontend/src/app/ui/dmp/editor/general-tab/general-tab.component.ts index 8063f5f60..0f699a41e 100644 --- a/dmp-frontend/src/app/ui/dmp/editor/general-tab/general-tab.component.ts +++ b/dmp-frontend/src/app/ui/dmp/editor/general-tab/general-tab.component.ts @@ -25,6 +25,7 @@ import { isNullOrUndefined } from 'util'; import { ConfigurationService } from '@app/core/services/configuration/configuration.service'; import { LanguageInfoService } from '@app/core/services/culture/language-info-service'; import { LanguageInfo } from '@app/core/model/language-info'; +import { LicenseCriteria } from '@app/core/query/license/license-criteria'; @Component({ selector: 'app-general-tab', @@ -67,6 +68,13 @@ export class GeneralTabComponent extends BaseComponent implements OnInit { titleFn: (item) => item['label'] }; + licenseAutoCompleteConfiguration: SingleAutoCompleteConfiguration = { + filterFn: this.licenseSearch.bind(this), + initialItems: (excludedItems: any[]) => this.licenseSearch('').pipe(map(result => result.filter(resultItem => (excludedItems || []).map(x => x.id).indexOf(resultItem.id) === -1))), + displayFn: (item) => item['name'], + titleFn: (item) => item['name'] + }; + selectedDmpProfileDefinition: DmpProfileDefinition; constructor( @@ -120,6 +128,14 @@ export class GeneralTabComponent extends BaseComponent implements OnInit { return this.dmpProfileService.getPaged(request).pipe(map(x => x.data)); } + licenseSearch(query: string): Observable { + const request = new RequestItem(); + request.criteria = new LicenseCriteria(); + request.criteria.like = query; + request.criteria.type = ''; + return this.externalSourcesService.searchLicense(request); + } + // onCallbackSuccess(): void { // this.uiNotificationService.snackBarNotification(this.isNew ? this.language.instant('GENERAL.SNACK-BAR.SUCCESSFUL-CREATION') : this.language.instant('GENERAL.SNACK-BAR.SUCCESSFUL-UPDATE'), SnackBarNotificationLevel.Success); // this.router.navigate(['/plans']); @@ -147,7 +163,7 @@ export class GeneralTabComponent extends BaseComponent implements OnInit { filterProfiles(value: string): Observable { - const request = new DataTableRequest(null, null, {fields: ['+label']}); + const request = new DataTableRequest(null, null, { fields: ['+label'] }); const criteria = new DatasetProfileCriteria(); criteria.like = value; request.criteria = criteria;