Add Authentication support for the remote fetcher and rework dataset template's autocomplete sources

This commit is contained in:
George Kalampokis 2023-10-06 18:07:58 +03:00
parent 922c2110ac
commit 70308db668
25 changed files with 570 additions and 66 deletions

View File

@ -51,6 +51,10 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/com.googlecode.json-simple/json-simple -->
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->

View File

@ -10,8 +10,12 @@ import eu.eudat.data.query.items.item.datasetprofile.DatasetProfileAutocompleteR
import eu.eudat.data.query.items.table.datasetprofile.DatasetProfileTableRequestItem;
import eu.eudat.exceptions.datasetprofile.DatasetProfileNewVersionException;
import eu.eudat.logic.builders.model.models.DataTableDataBuilder;
import eu.eudat.logic.proxy.config.Semantic;
import eu.eudat.logic.proxy.config.*;
import eu.eudat.logic.proxy.config.configloaders.ConfigLoader;
import eu.eudat.logic.proxy.config.entities.DatasetUrls;
import eu.eudat.logic.proxy.config.entities.GeneralUrls;
import eu.eudat.logic.proxy.config.entities.GenericUrls;
import eu.eudat.logic.proxy.fetching.RemoteFetcher;
import eu.eudat.logic.services.ApiContext;
import eu.eudat.logic.services.operations.DatabaseRepository;
import eu.eudat.logic.utilities.builders.XmlBuilder;
@ -56,6 +60,8 @@ import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.xpath.*;
import java.io.*;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.file.Files;
import java.util.*;
import java.util.stream.Collectors;
@ -66,22 +72,23 @@ public class DatasetProfileManager {
private static final Logger logger = LoggerFactory.getLogger(DatasetProfileManager.class);
private static final List<String> cache = new ArrayList<>();
private ApiContext apiContext;
private DatabaseRepository databaseRepository;
private Environment environment;
private ConfigLoader configLoader;
private final ApiContext apiContext;
private final DatabaseRepository databaseRepository;
private final Environment environment;
private final ConfigLoader configLoader;
private final MetricsManager metricsManager;
private final RemoteFetcher remoteFetcher;
private final DescriptionTemplateTypeService descriptionTemplateTypeService;
@Autowired
public DatasetProfileManager(ApiContext apiContext, Environment environment, ConfigLoader configLoader, MetricsManager metricsManager, DescriptionTemplateTypeService descriptionTemplateTypeService) {
public DatasetProfileManager(ApiContext apiContext, Environment environment, ConfigLoader configLoader, MetricsManager metricsManager, RemoteFetcher remoteFetcher, DescriptionTemplateTypeService descriptionTemplateTypeService) {
this.apiContext = apiContext;
this.databaseRepository = apiContext.getOperationsContext().getDatabaseRepository();
this.environment = environment;
this.configLoader = configLoader;
this.metricsManager = metricsManager;
this.descriptionTemplateTypeService = descriptionTemplateTypeService;
this.remoteFetcher = remoteFetcher;
}
@Transactional
@ -145,8 +152,8 @@ public class DatasetProfileManager {
return field;
}
public static List<ExternalAutocompleteFieldModel> getAutocomplete(AutoCompleteData data, String like) {
List<ExternalAutocompleteFieldModel> result = new LinkedList<>();
public List<ExternalAutocompleteFieldModel> getAutocomplete(AutoCompleteData data, String like) {
/*List<ExternalAutocompleteFieldModel> result = new LinkedList<>();
SimpleClientHttpRequestFactory simpleFactory = new SimpleClientHttpRequestFactory();
RestTemplate restTemplate = new RestTemplate(simpleFactory);
@ -209,8 +216,72 @@ public class DatasetProfileManager {
}
return result.stream().sorted(Comparator.comparing(ExternalAutocompleteFieldModel::getLabel)).collect(Collectors.toList());
//return result;
*/
List<ExternalAutocompleteFieldModel> result = new LinkedList<>();
ExternalUrlCriteria urlCriteria = new ExternalUrlCriteria();
GeneralUrls genericUrls = new GeneralUrls();
int ordinal = 1;
List<Map<String, String>> rawResults = new ArrayList<>();
genericUrls.setFetchMode(FetchStrategy.FIRST);
urlCriteria.setLike(like);
for (AutoCompleteData.AutoCompleteSingleData singleData : data.getAutoCompleteSingleDataList()) {
UrlConfiguration urlConfiguration = new UrlConfiguration();
try {
URI uri;
if (singleData.getUrl().contains("?")) {
uri = new URI(singleData.getUrl().substring(0, singleData.getUrl().lastIndexOf("?")));
} else {
uri = new URI(singleData.getUrl());
}
String source = singleData.getAutoCompleteOptions().getSource();
source = source != null && !source.isEmpty() ? source : uri.getHost();
String uriString = singleData.getAutoCompleteOptions().getUri();
uriString = uriString != null && !uriString.isEmpty() ? uriString : "uri";
String parsedUrl = singleData.getUrl();
parsedUrl = parsedUrl.replace("%20", " ");
parsedUrl = parsedUrl.replace("%22", "\"");
while (parsedUrl.contains("&amp;")) {
parsedUrl = parsedUrl.replace("&amp;", "&");
}
urlConfiguration.setUrl(parsedUrl);
urlConfiguration.setOrdinal(ordinal);
urlConfiguration.setType("External");
urlConfiguration.setContentType(MediaType.APPLICATION_JSON_VALUE);
urlConfiguration.setFirstpage("1");
urlConfiguration.setRequestType(singleData.getMethod() != null ? singleData.getMethod() : "GET");
DataUrlConfiguration dataUrlConfiguration = new DataUrlConfiguration();
dataUrlConfiguration.setPath(singleData.getOptionsRoot());
DataFieldsUrlConfiguration fieldsUrlConfiguration = new DataFieldsUrlConfiguration();
fieldsUrlConfiguration.setId(singleData.getAutoCompleteOptions().getValue());
fieldsUrlConfiguration.setName(singleData.getAutoCompleteOptions().getLabel());
fieldsUrlConfiguration.setSource(singleData.getAutoCompleteOptions().getSource().isEmpty()? null : singleData.getAutoCompleteOptions().getSource());
fieldsUrlConfiguration.setUri(uriString);
dataUrlConfiguration.setFieldsUrlConfiguration(fieldsUrlConfiguration);
urlConfiguration.setKey(source);
urlConfiguration.setLabel(source);
urlConfiguration.setData(dataUrlConfiguration);
if (singleData.getHasAuth()) {
AuthenticationConfiguration authenticationConfiguration = new AuthenticationConfiguration();
authenticationConfiguration.setAuthUrl(singleData.getAuth().getUrl());
authenticationConfiguration.setAuthMethod(singleData.getAuth().getMethod());
authenticationConfiguration.setAuthTokenPath(singleData.getAuth().getPath());
authenticationConfiguration.setAuthRequestBody(singleData.getAuth().getBody());
authenticationConfiguration.setType(singleData.getAuth().getType());
urlConfiguration.setAuth(authenticationConfiguration);
}
genericUrls.getUrls().add(urlConfiguration);
List<Map<String, String>> singleResults = this.remoteFetcher.getExternalGeneric(urlCriteria, genericUrls);
if (!singleResults.isEmpty() && !singleResults.get(0).containsKey("source") && !singleData.getAutoCompleteOptions().getSource().isEmpty()) {
singleResults.forEach(singleResult -> singleResult.put("source", singleData.getAutoCompleteOptions().getSource()));
}
rawResults.addAll(singleResults);
genericUrls.getUrls().clear();
} catch (URISyntaxException e) {
logger.error(e.getMessage(), e);
}
}
rawResults.forEach(item -> result.add(new ExternalAutocompleteFieldModel(parseItem(item.get("pid")), parseItem(item.get("name")), parseItem(item.get("source")), parseItem(item.get("uri")))));
return result;
}
private static String parseItem(Object item) {

View File

@ -27,11 +27,13 @@ public class PrefillingManager {
private final ObjectMapper objectMapper;
private final DatasetManager datasetManager;
private final LicenseManager licenseManager;
private final PrefillingMapper prefillingMapper;
@Autowired
public PrefillingManager(ApiContext apiContext, ConfigLoader configLoader, DatasetManager datasetManager, LicenseManager licenseManager) {
public PrefillingManager(ApiContext apiContext, ConfigLoader configLoader, DatasetManager datasetManager, LicenseManager licenseManager, PrefillingMapper prefillingMapper) {
this.apiContext = apiContext;
this.configLoader = configLoader;
this.prefillingMapper = prefillingMapper;
this.objectMapper = new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
this.datasetManager = datasetManager;
this.licenseManager = licenseManager;
@ -62,14 +64,14 @@ public class PrefillingManager {
PrefillingGet prefillingGet = prefillingConfig.getPrefillingGet();
Map<String, Object> prefillingEntity = getSingle(prefillingGet.getUrl(), prefillId);
DescriptionTemplate descriptionTemplate = apiContext.getOperationsContext().getDatabaseRepository().getDatasetProfileDao().find(profileId);
return PrefillingMapper.mapPrefilledEntityToDatasetWizard(prefillingEntity, prefillingGet, prefillingConfig.getType(), descriptionTemplate, datasetManager, licenseManager);
return prefillingMapper.mapPrefilledEntityToDatasetWizard(prefillingEntity, prefillingGet, prefillingConfig.getType(), descriptionTemplate, datasetManager, licenseManager);
}
public DatasetWizardModel getPrefilledDatasetUsingData(Map<String, Object> data, String configId, UUID profileId) throws Exception {
PrefillingConfig prefillingConfig = configLoader.getExternalUrls().getPrefillings().get(configId);
PrefillingGet prefillingGet = prefillingConfig.getPrefillingGet();
DescriptionTemplate descriptionTemplate = apiContext.getOperationsContext().getDatabaseRepository().getDatasetProfileDao().find(profileId);
return PrefillingMapper.mapPrefilledEntityToDatasetWizard(data, prefillingGet, prefillingConfig.getType(), descriptionTemplate, datasetManager, licenseManager);
return prefillingMapper.mapPrefilledEntityToDatasetWizard(data, prefillingGet, prefillingConfig.getType(), descriptionTemplate, datasetManager, licenseManager);
}
private Map<String, Object> getSingle(String url, String id) {

View File

@ -27,6 +27,8 @@ import eu.eudat.models.data.license.LicenseModel;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
@ -35,11 +37,18 @@ import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;
@Component
public class PrefillingMapper {
private static final Logger logger = LoggerFactory.getLogger(PrefillingMapper.class);
private static final ObjectMapper mapper = new ObjectMapper().configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
private final DatasetProfileManager datasetProfileManager;
public static DatasetWizardModel mapPrefilledEntityToDatasetWizard(Map<String, Object> prefilledEntity, PrefillingGet prefillingGet, String type,
@Autowired
public PrefillingMapper(DatasetProfileManager datasetProfileManager) {
this.datasetProfileManager = datasetProfileManager;
}
public DatasetWizardModel mapPrefilledEntityToDatasetWizard(Map<String, Object> prefilledEntity, PrefillingGet prefillingGet, String type,
DescriptionTemplate profile, DatasetManager datasetManager, LicenseManager licenseManager) throws Exception {
DatasetWizardModel datasetWizardModel = new DatasetWizardModel();
datasetWizardModel.setProfile(new DatasetProfileOverviewModel().fromDataModel(profile));
@ -77,7 +86,7 @@ public class PrefillingMapper {
return datasetWizardModel;
}
private static void setValue(PrefillingMapping prefillingMapping, String value, DatasetWizardModel datasetWizardModel, JsonNode parentNode, Map<String, Object> properties, String type, LicenseManager licenseManager) throws InvocationTargetException, IllegalAccessException, JsonProcessingException {
private void setValue(PrefillingMapping prefillingMapping, String value, DatasetWizardModel datasetWizardModel, JsonNode parentNode, Map<String, Object> properties, String type, LicenseManager licenseManager) throws InvocationTargetException, IllegalAccessException, JsonProcessingException {
String trimRegex = prefillingMapping.getTrimRegex() != null ? prefillingMapping.getTrimRegex() : "";
if (!value.startsWith("\"") && !value.startsWith("[") && !value.equals("null")) {
value = "\"" + value + "\"";
@ -216,7 +225,7 @@ public class PrefillingMapper {
}
}
private static Object parseComboBoxValues(JsonNode node, List<String> parsedValues) throws JsonProcessingException {
private Object parseComboBoxValues(JsonNode node, List<String> parsedValues) throws JsonProcessingException {
List<Object> normalizedValues = new ArrayList<>();
boolean isMultiSelect;
String type = node.isArray() ? node.get(0).get("data").get("type").asText() : node.get("data").get("type").asText();
@ -227,7 +236,7 @@ public class PrefillingMapper {
for (String format : parsedValues) {
List<ExternalAutocompleteFieldModel> result = new ArrayList<>();
try {
result = DatasetProfileManager.getAutocomplete(autoCompleteData, format);
result = datasetProfileManager.getAutocomplete(autoCompleteData, format);
}
catch (Exception e) {
logger.error(e.getMessage(), e);

View File

@ -0,0 +1,23 @@
package eu.eudat.logic.proxy.config;
public enum AuthType {
;
private final String name;
AuthType(String name) {
this.name = name;
}
public String getName() {
return name;
}
public static AuthType fromName(String name) {
for (AuthType authType : AuthType.values()) {
if (authType.getName().equals(name)) {
return authType;
}
}
throw new IllegalArgumentException("AuthType [" + name + "] is not supported");
}
}

View File

@ -0,0 +1,57 @@
package eu.eudat.logic.proxy.config;
import jakarta.xml.bind.annotation.XmlElement;
public class AuthenticationConfiguration {
private String authUrl;
private String authMethod = "GET";
private String authTokenPath;
private String authRequestBody;
private String type;
public String getAuthUrl() {
return authUrl;
}
@XmlElement(name = "authUrl")
public void setAuthUrl(String authUrl) {
this.authUrl = authUrl;
}
public String getAuthMethod() {
return authMethod;
}
@XmlElement(name = "authUrlMethod")
public void setAuthMethod(String authMethod) {
this.authMethod = authMethod;
}
public String getAuthTokenPath() {
return authTokenPath;
}
@XmlElement(name = "authTokenJpath")
public void setAuthTokenPath(String authTokenPath) {
this.authTokenPath = authTokenPath;
}
public String getAuthRequestBody() {
return authRequestBody;
}
@XmlElement(name = "authUrlBody")
public void setAuthRequestBody(String authRequestBody) {
this.authRequestBody = authRequestBody;
}
public String getType() {
return type;
}
@XmlElement(name = "authType")
public void setType(String type) {
this.type = type;
}
}

View File

@ -21,6 +21,7 @@ public class UrlConfiguration {
private String requestType = "GET";
private String requestBody = "";
private String filterType = "remote";
private AuthenticationConfiguration auth;
private List<QueryConfig> queries;
@ -143,4 +144,13 @@ public class UrlConfiguration {
public void setQueries(List<QueryConfig> queries) {
this.queries = queries;
}
public AuthenticationConfiguration getAuth() {
return auth;
}
@XmlElement(name="authentication")
public void setAuth(AuthenticationConfiguration auth) {
this.auth = auth;
}
}

View File

@ -0,0 +1,31 @@
package eu.eudat.logic.proxy.config.entities;
import eu.eudat.logic.proxy.config.FetchStrategy;
import eu.eudat.logic.proxy.config.UrlConfiguration;
import java.util.ArrayList;
import java.util.List;
public class GeneralUrls extends GenericUrls{
List<UrlConfiguration> urls;
FetchStrategy fetchMode;
public GeneralUrls() {
this.urls = new ArrayList<>();
}
@Override
public List<UrlConfiguration> getUrls() {
return urls;
}
@Override
public FetchStrategy getFetchMode() {
return fetchMode;
}
public void setFetchMode(FetchStrategy fetchMode) {
this.fetchMode = fetchMode;
}
}

View File

@ -11,16 +11,21 @@ import eu.eudat.logic.proxy.config.entities.GenericUrls;
import eu.eudat.logic.proxy.config.exceptions.HugeResultSet;
import eu.eudat.logic.proxy.config.exceptions.NoURLFound;
import eu.eudat.logic.proxy.fetching.entities.Results;
import jakarta.xml.bind.JAXBContext;
import jakarta.xml.bind.Unmarshaller;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.*;
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
import org.springframework.http.codec.json.Jackson2JsonDecoder;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.netty.http.client.HttpClient;
import jakarta.xml.bind.JAXBContext;
import jakarta.xml.bind.Unmarshaller;
import java.io.File;
import java.io.StringReader;
import java.lang.reflect.Method;
@ -33,10 +38,16 @@ public class RemoteFetcher {
private static final Logger logger = LoggerFactory.getLogger(RemoteFetcher.class);
private ConfigLoader configLoader;
private final WebClient client;
@Autowired
public RemoteFetcher(ConfigLoader configLoader) {
this.configLoader = configLoader;
this.client = WebClient.builder().codecs(clientCodecConfigurer -> {
clientCodecConfigurer.defaultCodecs().jackson2JsonDecoder(new Jackson2JsonDecoder(new ObjectMapper(), MediaType.APPLICATION_JSON));
clientCodecConfigurer.defaultCodecs().maxInMemorySize(2 * ((int) Math.pow(1024, 3))); //GK: Why here???
}
).clientConnector(new ReactorClientHttpConnector(HttpClient.create().followRedirect(true))).build();
}
@Cacheable(value = "repositories", keyGenerator = "externalUrlsKeyGenerator")
@ -198,7 +209,11 @@ public class RemoteFetcher {
ifFunderQueryExist(urlConfiguration, externalUrlCriteria);
if (urlConfiguration.getType() == null || urlConfiguration.getType().equals("External")) {
try {
results.addAll(getAllResultsFromUrl(urlConfiguration.getUrl(), fetchStrategy, urlConfiguration.getData(), urlConfiguration.getPaginationPath(), externalUrlCriteria, urlConfiguration.getLabel(), urlConfiguration.getKey(), urlConfiguration.getContentType(), urlConfiguration.getFirstpage(), urlConfiguration.getRequestBody(), urlConfiguration.getRequestType(), urlConfiguration.getFilterType(), urlConfiguration.getQueries()));
String auth = null;
if (urlConfiguration.getAuth() != null) {
auth = this.getAuthentication(urlConfiguration.getAuth());
}
results.addAll(getAllResultsFromUrl(urlConfiguration.getUrl(), fetchStrategy, urlConfiguration.getData(), urlConfiguration.getPaginationPath(), externalUrlCriteria, urlConfiguration.getLabel(), urlConfiguration.getKey(), urlConfiguration.getContentType(), urlConfiguration.getFirstpage(), urlConfiguration.getRequestBody(), urlConfiguration.getRequestType(), urlConfiguration.getFilterType(), urlConfiguration.getQueries(), auth));
} catch (Exception e) {
logger.error(e.getLocalizedMessage(), e);
}
@ -217,6 +232,19 @@ public class RemoteFetcher {
return results;
}
private String getAuthentication(AuthenticationConfiguration authenticationConfiguration) {
HttpMethod method = HttpMethod.valueOf(authenticationConfiguration.getAuthMethod());
Map<String, Object> reponse = this.client.method(method).uri(authenticationConfiguration.getAuthUrl())
.contentType(MediaType.APPLICATION_JSON)
.bodyValue(this.parseBodyString(authenticationConfiguration.getAuthRequestBody()))
.exchangeToMono(mono -> mono.bodyToMono(new ParameterizedTypeReference<Map<String, Object>>() {
})).block();
return authenticationConfiguration.getType() + " " + reponse.get(authenticationConfiguration.getAuthTokenPath());
}
private List<Map<String, Object>> getAllWithData(List<UrlConfiguration> urlConfigs, ExternalUrlCriteria externalUrlCriteria) {
List<Map<String, Object>> results = new LinkedList<>();
@ -273,7 +301,7 @@ public class RemoteFetcher {
protected String replaceCriteriaOnUrl(String path, ExternalUrlCriteria externalUrlCriteria, String firstPage, List<QueryConfig> queries) {
String completedPath = path;
if (externalUrlCriteria.getLike() != null) {
if ((path.contains("openaire") || path.contains("orcid") || path.contains("ror")) && externalUrlCriteria.getLike().equals("")) {
if ((path.contains("openaire") || path.contains("orcid") || path.contains("ror") || path.contains("fairsharing")) && externalUrlCriteria.getLike().equals("")) {
completedPath = completedPath.replaceAll("\\{like}", "*");
completedPath = completedPath.replaceAll("\\{query}", "*");
} else {
@ -329,13 +357,13 @@ public class RemoteFetcher {
return completedPath;
}
private List<Map<String, String>> getAllResultsFromUrl(String path, FetchStrategy fetchStrategy, final DataUrlConfiguration jsonDataPath, final String jsonPaginationPath, ExternalUrlCriteria externalUrlCriteria, String tag, String key, String contentType, String firstPage, String requestBody, String requestType, String filterType, List<QueryConfig> queries) throws Exception {
private List<Map<String, String>> getAllResultsFromUrl(String path, FetchStrategy fetchStrategy, final DataUrlConfiguration jsonDataPath, final String jsonPaginationPath, ExternalUrlCriteria externalUrlCriteria, String tag, String key, String contentType, String firstPage, String requestBody, String requestType, String filterType, List<QueryConfig> queries, String auth) throws Exception {
Set<Integer> pages = new HashSet<>();
String replacedPath = replaceCriteriaOnUrl(path, externalUrlCriteria, firstPage, queries);
String replacedBody = replaceCriteriaOnUrl(requestBody, externalUrlCriteria, firstPage, queries);
Results results = getResultsFromUrl(replacedPath, jsonDataPath, jsonPaginationPath, contentType, replacedBody, requestType);
Results results = getResultsFromUrl(replacedPath, jsonDataPath, jsonPaginationPath, contentType, replacedBody, requestType, auth);
if(results != null) {
if (filterType != null && filterType.equals("local") && (externalUrlCriteria.getLike() != null && !externalUrlCriteria.getLike().isEmpty())) {
results.setResults(results.getResults().stream()
@ -354,7 +382,7 @@ public class RemoteFetcher {
throw new HugeResultSet("The submitted search query " + externalUrlCriteria.getLike() + " is about to return " + results.getPagination().get("count") + " results... Please submit a more detailed search query");
Optional<Results> optionalResults = pages.parallelStream()
.map(page -> getResultsFromUrl(path + "&page=" + page, jsonDataPath, jsonPaginationPath, contentType, replacedBody, requestType))
.map(page -> getResultsFromUrl(path + "&page=" + page, jsonDataPath, jsonPaginationPath, contentType, replacedBody, requestType, auth))
.filter(Objects::nonNull)
.reduce((result1, result2) -> {
result1.getResults().addAll(result2.getResults());
@ -387,7 +415,7 @@ public class RemoteFetcher {
JsonNode jsonBody = new ObjectMapper().readTree(replacedBody);
entity = new HttpEntity<>(jsonBody, headers);
response = restTemplate.exchange(replacedPath, HttpMethod.resolve(requestType), entity, String.class);
response = restTemplate.exchange(replacedPath, HttpMethod.valueOf(requestType), entity, String.class);
if (response.getStatusCode() == HttpStatus.OK) {
if (response.getHeaders().get("Content-Type").get(0).contains("json")) {
DocumentContext jsonContext = JsonPath.parse(response.getBody());
@ -403,12 +431,12 @@ public class RemoteFetcher {
}
protected Results getResultsFromUrl(String urlString, DataUrlConfiguration jsonDataPath, String jsonPaginationPath, String contentType, String requestBody, String requestType) {
protected Results getResultsFromUrl(String urlString, DataUrlConfiguration jsonDataPath, String jsonPaginationPath, String contentType, String requestBody, String requestType, String auth) {
try {
RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
HttpEntity<JsonNode> entity;
//RestTemplate restTemplate = new RestTemplate(new SimpleClientHttpRequestFactory());
//HttpHeaders headers = new HttpHeaders();
//HttpEntity<JsonNode> entity;
ResponseEntity<String> response;
/*
* URL url = new URL(urlString.replaceAll(" ", "%20"));
@ -416,14 +444,27 @@ public class RemoteFetcher {
* HttpURLConnection con = (HttpURLConnection) url.openConnection();
* con.setRequestMethod("GET");
*/
if (contentType != null && !contentType.isEmpty()) {
/* if (contentType != null && !contentType.isEmpty()) {
headers.setAccept(Collections.singletonList(MediaType.valueOf(contentType)));
headers.setContentType(MediaType.valueOf(contentType));
}
if (auth != null) {
headers.set("Authorization", auth);
}*/
JsonNode jsonBody = new ObjectMapper().readTree(requestBody);
entity = new HttpEntity<>(jsonBody, headers);
// entity = new HttpEntity<>(jsonBody, headers);
response = restTemplate.exchange(urlString, HttpMethod.resolve(requestType), entity, String.class);
response = this.client.method(HttpMethod.valueOf(requestType)).uri(urlString).headers(httpHeaders -> {
if (contentType != null && !contentType.isEmpty()) {
httpHeaders.setAccept(Collections.singletonList(MediaType.valueOf(contentType)));
httpHeaders.setContentType(MediaType.valueOf(contentType));
}
if (auth != null) {
httpHeaders.set("Authorization", auth);
}
}).bodyValue(jsonBody).retrieve().toEntity(String.class).block();
//response = restTemplate.exchange(urlString, HttpMethod.resolve(requestType), entity, String.class);
if (response.getStatusCode() == HttpStatus.OK) { // success
//do here all the parsing
Results results = new Results();
@ -431,7 +472,7 @@ public class RemoteFetcher {
DocumentContext jsonContext = JsonPath.parse(response.getBody());
if (jsonDataPath.getFieldsUrlConfiguration().getPath() != null) {
results = RemoteFetcherUtils.getFromJsonWithRecursiveFetching(jsonContext, jsonDataPath, this, requestBody, requestType);
results = RemoteFetcherUtils.getFromJsonWithRecursiveFetching(jsonContext, jsonDataPath, this, requestBody, requestType, auth);
} else if (jsonDataPath.getFieldsUrlConfiguration().getFirstName() != null) {
results = RemoteFetcherUtils.getFromJsonWithFirstAndLastName(jsonContext, jsonDataPath);
} else {
@ -553,8 +594,18 @@ public class RemoteFetcher {
return null;
}
private String parseBodyString(String bodyString) {
String finalBodyString = bodyString;
if (bodyString.contains("{env:")) {
int index = bodyString.indexOf("{env: ");
while (index >= 0) {
int endIndex = bodyString.indexOf("}", index + 6);
String envName = bodyString.substring(index + 6, endIndex);
finalBodyString = finalBodyString.replace("{env: " + envName + "}", System.getenv(envName));
index = bodyString.indexOf("{env: ", index + 6);
}
}
return finalBodyString;
}
}

View File

@ -28,7 +28,7 @@ public class RemoteFetcherUtils {
new HashMap<>(1, 1));
}
public static Results getFromJsonWithRecursiveFetching(DocumentContext jsonContext, DataUrlConfiguration jsonDataPath, RemoteFetcher remoteFetcher, String requestBody, String requestType) {
public static Results getFromJsonWithRecursiveFetching(DocumentContext jsonContext, DataUrlConfiguration jsonDataPath, RemoteFetcher remoteFetcher, String requestBody, String requestType, String auth) {
Results results = new Results(parseData(jsonContext, jsonDataPath),
new HashMap<>(1, 1));
@ -37,7 +37,7 @@ public class RemoteFetcherUtils {
externalUrlCriteria.setPath(result.get("path"));
externalUrlCriteria.setHost(result.get("host"));
String replacedPath = remoteFetcher.replaceCriteriaOnUrl(jsonDataPath.getUrlConfiguration().getUrl(), externalUrlCriteria, jsonDataPath.getUrlConfiguration().getFirstpage(), jsonDataPath.getUrlConfiguration().getQueries());
return remoteFetcher.getResultsFromUrl(replacedPath, jsonDataPath.getUrlConfiguration().getData(), jsonDataPath.getUrlConfiguration().getData().getPath(), jsonDataPath.getUrlConfiguration().getContentType(), requestBody, requestType);
return remoteFetcher.getResultsFromUrl(replacedPath, jsonDataPath.getUrlConfiguration().getData(), jsonDataPath.getUrlConfiguration().getData().getPath(), jsonDataPath.getUrlConfiguration().getContentType(), requestBody, requestType, auth);
}).filter(Objects::nonNull).map(results1 -> results1.getResults().get(0)).collect(Collectors.toList());
return new Results(multiResults, new HashMap<>(1, 1));
}
@ -98,11 +98,25 @@ public class RemoteFetcherUtils {
}
} else {
value = value.replace("'", "");
if (value.contains(".")) {
String[] parts = value.split("\\.");
Map<String, Object> tempMap = stringObjectMap;
for (int i = 0; i < parts.length; i++) {
if (tempMap.containsKey(parts[i])) {
if (i + 1 < parts.length) {
tempMap = (Map<String, Object>) tempMap.get(parts[i]);
} else {
parsedData.get(parsedData.size() - 1).put(field.getName().equals("types") ? "tags" : value, normalizeValue(tempMap.get(parts[i]), (field.getName().equals("types") || field.getName().equals("uri"))));
}
}
}
} else {
if (stringObjectMap.containsKey(value)) {
parsedData.get(parsedData.size() - 1).put(field.getName().equals("types") ? "tags" : value, normalizeValue(stringObjectMap.get(value), (field.getName().equals("types") || field.getName().equals("uri"))));
}
}
}
}
} catch (IllegalAccessException | InvocationTargetException e) {
logger.error(e.getLocalizedMessage(), e);
}
@ -135,7 +149,8 @@ public class RemoteFetcherUtils {
}
}
} else if (value instanceof Map) {
return ((Map<String, String>)value).get("content");
String key = ((Map<String, String>)value).containsKey("$") ? "$" : "content";
return ((Map<String, String>)value).get(key);
}
return value != null ? value.toString() : null;
}

View File

@ -10,11 +10,62 @@ import java.util.List;
import java.util.Map;
public class AutoCompleteData extends ComboBoxData<AutoCompleteData> {
public static class AuthAutoCompleteData {
private String url;
private String method;
private String body;
private String path;
private String type;
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getMethod() {
return method;
}
public void setMethod(String method) {
this.method = method;
}
public String getBody() {
return body;
}
public void setBody(String body) {
this.body = body;
}
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
}
public static class AutoCompleteSingleData {
private int autocompleteType;
private String url;
private ComboBoxData.Option autoCompleteOptions;
private String optionsRoot;
private Boolean hasAuth;
private AuthAutoCompleteData auth;
private String method;
public int getAutocompleteType() {
return autocompleteType;
@ -38,12 +89,36 @@ public class AutoCompleteData extends ComboBoxData<AutoCompleteData> {
this.url = url;
}
public Boolean getHasAuth() {
return hasAuth;
}
public void setHasAuth(Boolean hasAuth) {
this.hasAuth = hasAuth;
}
public AuthAutoCompleteData getAuth() {
return auth;
}
public void setAuth(AuthAutoCompleteData auth) {
this.auth = auth;
}
public ComboBoxData.Option getAutoCompleteOptions() {
return autoCompleteOptions;
}
public void setAutoCompleteOptions(ComboBoxData.Option autoCompleteOptions) {
this.autoCompleteOptions = autoCompleteOptions;
}
public String getMethod() {
return method;
}
public void setMethod(String method) {
this.method = method;
}
}
private Boolean multiAutoComplete;
@ -70,11 +145,22 @@ public class AutoCompleteData extends ComboBoxData<AutoCompleteData> {
parent.setAttribute("url", singleData.url);
parent.setAttribute("optionsRoot", singleData.optionsRoot);
parent.setAttribute("autoCompleteType", Integer.toString(singleData.autocompleteType));
parent.setAttribute("hasAuth", Boolean.toString(singleData.hasAuth));
parent.setAttribute("method", singleData.method);
Element element = doc.createElement("option");
element.setAttribute("label", singleData.autoCompleteOptions.getLabel());
element.setAttribute("value", singleData.autoCompleteOptions.getValue());
element.setAttribute("source", singleData.autoCompleteOptions.getSource());
parent.appendChild(element);
if (singleData.hasAuth) {
Element authElement = doc.createElement("auth");
authElement.setAttribute("url", singleData.auth.url);
authElement.setAttribute("method", singleData.auth.method);
authElement.setAttribute("body", singleData.auth.body);
authElement.setAttribute("path", singleData.auth.path);
authElement.setAttribute("type", singleData.auth.type);
parent.appendChild(authElement);
}
root.appendChild(parent);
}
return root;
@ -108,6 +194,8 @@ public class AutoCompleteData extends ComboBoxData<AutoCompleteData> {
} else {
singleData.autocompleteType = AutocompleteType.fromValue(Integer.parseInt(item.getAttribute("autoCompleteType"))).getValue();
}
singleData.hasAuth = Boolean.parseBoolean(item.getAttribute("hasAuth"));
singleData.method = item.hasAttribute("method") ? item.getAttribute("method") : "GET";
Element optionElement = (Element) item.getElementsByTagName("option").item(0);
if (optionElement != null) {
singleData.autoCompleteOptions = new Option();
@ -116,6 +204,17 @@ public class AutoCompleteData extends ComboBoxData<AutoCompleteData> {
singleData.autoCompleteOptions.setSource(optionElement.getAttribute("source"));
singleData.autoCompleteOptions.setUri(optionElement.getAttribute("uri"));
}
if (singleData.hasAuth) {
Element authElement = (Element) item.getElementsByTagName("auth").item(0);
if (authElement != null) {
singleData.auth = new AuthAutoCompleteData();
singleData.auth.setUrl(authElement.getAttribute("url"));
singleData.auth.setMethod(authElement.getAttribute("method"));
singleData.auth.setBody(authElement.getAttribute("body"));
singleData.auth.setPath(authElement.getAttribute("path"));
singleData.auth.setType(authElement.getAttribute("type"));
}
}
}
@Override
@ -141,6 +240,8 @@ public class AutoCompleteData extends ComboBoxData<AutoCompleteData> {
this.autoCompleteSingleDataList.get(i).autoCompleteOptions = new Option();
this.autoCompleteSingleDataList.get(i).url = (String) singleData.get("url");
this.autoCompleteSingleDataList.get(i).optionsRoot = (String) singleData.get("optionsRoot");
this.autoCompleteSingleDataList.get(i).hasAuth = (Boolean) singleData.get("hasAuth");
this.autoCompleteSingleDataList.get(i).method = singleData.containsKey("method") ? (String) singleData.get("method") : "GET";
if (singleData.get("autoCompleteType") == null) {
this.autoCompleteSingleDataList.get(i).autocompleteType = AutocompleteType.UNCACHED.getValue();
@ -154,6 +255,17 @@ public class AutoCompleteData extends ComboBoxData<AutoCompleteData> {
this.autoCompleteSingleDataList.get(i).autoCompleteOptions.setSource(options.get("source"));
this.autoCompleteSingleDataList.get(i).autoCompleteOptions.setUri(options.get("uri"));
}
if (this.autoCompleteSingleDataList.get(i).hasAuth) {
Map<String, String> auth = (Map<String, String>) singleData.get("auth");
if (auth != null) {
this.autoCompleteSingleDataList.get(i).auth = new AuthAutoCompleteData();
this.autoCompleteSingleDataList.get(i).auth.setUrl(auth.get("url"));
this.autoCompleteSingleDataList.get(i).auth.setType(auth.get("type"));
this.autoCompleteSingleDataList.get(i).auth.setPath(auth.get("path"));
this.autoCompleteSingleDataList.get(i).auth.setBody(auth.get("body"));
this.autoCompleteSingleDataList.get(i).auth.setMethod(auth.get("method"));
}
}
i++;
}
}
@ -190,6 +302,8 @@ public class AutoCompleteData extends ComboBoxData<AutoCompleteData> {
node.appendChild(autoCompleteSingles.item(i));
node.setAttribute("url", item.getAttribute("url"));
node.setAttribute("optionsRoot", item.getAttribute("optionsRoot"));
node.setAttribute("hasAuth", item.getAttribute("hasAuth"));
node.setAttribute("method", item.hasAttribute("method") ? item.getAttribute("method") : "GET");
autoCompletes.add(singleToMap(node));
}
}
@ -214,6 +328,16 @@ public class AutoCompleteData extends ComboBoxData<AutoCompleteData> {
return dataMap;
}
private Map<String, Object> authToMap(Element item){
HashMap dataMap = new HashMap();
dataMap.put("url", item != null ? item.getAttribute("url") : "");
dataMap.put("method", item != null ? item.getAttribute("method") : "");
dataMap.put("body", item != null ? item.getAttribute("body") : "");
dataMap.put("path", item != null ? item.getAttribute("path") : "");
dataMap.put("type", item != null ? item.getAttribute("type") : "");
return dataMap;
}
private Map<String, Object> singleToMap(Element item) {
Map<String, Object> dataMap = new HashMap<>();
if (!item.getAttribute("autoCompleteType").isEmpty()) {
@ -221,8 +345,12 @@ public class AutoCompleteData extends ComboBoxData<AutoCompleteData> {
}
dataMap.put("optionsRoot", item != null ? item.getAttribute("optionsRoot") : "");
dataMap.put("url", item != null ? item.getAttribute("url") : "");
dataMap.put("hasAuth", item != null ? item.getAttribute("hasAuth") : "false");
Element optionElement = (Element) item.getElementsByTagName("option").item(0);
dataMap.put("autoCompleteOptions", item != null ? optionToMap(optionElement) : null);
Element authElement = (Element) item.getElementsByTagName("auth").item(0);
dataMap.put("auth", item != null ? authToMap(authElement) : null);
dataMap.put("method", item != null && item.hasAttribute("method") ? item.getAttribute("method") : "GET");
return dataMap;
}

View File

@ -12,11 +12,22 @@ export interface AutoCompleteFieldData extends FieldData {
multiAutoComplete: boolean;
}
export interface AuthAutoCompleteData extends FieldData {
url: string;
method: string;
body: string;
path: string;
type: string;
}
export interface AutoCompleteSingleData extends FieldData {
url: string;
optionsRoot: string;
autoCompleteOptions: FieldDataOption;
autocompleteType: number;
hasAuth: boolean;
method: string;
auth: AuthAutoCompleteData;
}
export interface CheckBoxFieldData extends FieldData {

View File

@ -3,6 +3,7 @@ import { DatasetProfileComboBoxType } from '@app/core/common/enum/dataset-profil
import { FieldDataOptionEditorModel } from './field-data-option-editor-model';
import { UntypedFormGroup, Validators } from '@angular/forms';
import { AutoCompleteFieldData, AutoCompleteSingleData } from '@app/core/model/dataset-profile-definition/field-data/field-data';
import { AuthFieldEditorModel } from './auto-complete-auth-field-data.model';
export class AutoCompleteSingleDataEditorModel extends FieldDataEditorModel<AutoCompleteSingleDataEditorModel> {
@ -11,6 +12,9 @@ export class AutoCompleteSingleDataEditorModel extends FieldDataEditorModel<Auto
public autoCompleteOptions: FieldDataOptionEditorModel = new FieldDataOptionEditorModel();
public autoCompleteType: number;
public method: string;
public hasAuth: boolean;
public auth: AuthFieldEditorModel = new AuthFieldEditorModel();
//public multiAutoCompleteOptions: FieldDataOptionEditorModel = new FieldDataOptionEditorModel();
buildForm(disabled: boolean = false, skipDisable: Array<String> = []): UntypedFormGroup {
@ -18,9 +22,12 @@ export class AutoCompleteSingleDataEditorModel extends FieldDataEditorModel<Auto
label: [{ value: this.label, disabled: (disabled && !skipDisable.includes('AutoCompleteSingleDataEditorModel.label')) }],
url: [{ value: this.url, disabled: (disabled && !skipDisable.includes('AutoCompleteSingleDataEditorModel.url')) },[Validators.required]],
optionsRoot: [{ value: this.optionsRoot, disabled: (disabled && !skipDisable.includes('AutoCompleteSingleDataEditorModel.optionsRoot')) }, [Validators.required]],
autoCompleteType: [{ value: this.autoCompleteType, disabled: (disabled && !skipDisable.includes('AutoCompleteSingleDataEditorModel.autoCompleteType')) }]
autoCompleteType: [{ value: this.autoCompleteType, disabled: (disabled && !skipDisable.includes('AutoCompleteSingleDataEditorModel.autoCompleteType')) }],
hasAuth: [{ value: this.hasAuth, disabled: (disabled && !skipDisable.includes('AutoCompleteSingleDataEditorModel.hasAuth')) }],
method: [{ value: this.method, disabled: (disabled && !skipDisable.includes('AutoCompleteSingleDataEditorModel.method')) }]
});
formGroup.addControl('autoCompleteOptions', this.autoCompleteOptions.buildForm(disabled, skipDisable));
formGroup.addControl('auth', this.auth.buildForm(disabled, skipDisable));
return formGroup;
}
@ -30,7 +37,10 @@ export class AutoCompleteSingleDataEditorModel extends FieldDataEditorModel<Auto
this.label = item.label;
this.optionsRoot = item.optionsRoot;
this.autoCompleteType = item.autocompleteType;
this.hasAuth = item.hasAuth;
this.method = item.method ? item.method : 'GET';
this.autoCompleteOptions = new FieldDataOptionEditorModel().fromModel(item.autoCompleteOptions);
this.auth = new AuthFieldEditorModel().fromModel(item.auth);
return this;
}
}

View File

@ -37,15 +37,21 @@
</div>
<div *ngFor="let singleForm of multiForm.controls; let i = index" class="row">
<mat-form-field class="col-12">
<!-- <mat-form-field class="col-12">
<mat-label>{{'DATASET-PROFILE-EDITOR.STEPS.FORM.FIELD.FIELDS.FIELD-AUTOCOMPLETE-TYPE' | translate}}</mat-label>
<mat-select [formControl]="singleForm.get('autoCompleteType')">
<mat-option [value]="0">{{'DATASET-PROFILE-EDITOR.STEPS.FORM.FIELD.FIELDS.FIELD-AUTOCOMPLETE-TYPE-UNCACHED' | translate}}</mat-option>
<mat-option [value]="1">{{'DATASET-PROFILE-EDITOR.STEPS.FORM.FIELD.FIELDS.FIELD-AUTOCOMPLETE-TYPE-CACHED' | translate}}</mat-option>
</mat-select>
</mat-form-field> -->
<mat-form-field class="col-md-6">
<mat-label>{{'DATASET-PROFILE-EDITOR.STEPS.FORM.FIELD.FIELDS.FIELD-AUTOCOMPLETE-AUTH-METHOD' | translate}}</mat-label>
<mat-select [formControl]="singleForm.get('method')">
<mat-option *ngFor="let method of htmlMethods | keyvalue" [value]="method.value">{{method.value}}</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field class="col-md-12">
<mat-form-field class="col-md-6">
<mat-label>{{'DATASET-PROFILE-EDITOR.STEPS.FORM.FIELD.FIELDS.FIELD-AUTOCOMPLETE-URL' | translate}}</mat-label>
<input matInput [formControl]="singleForm.get('url')">
<mat-error *ngIf="singleForm.get('url').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
@ -70,6 +76,37 @@
<input matInput [formControl]="singleForm.get('autoCompleteOptions').get('source')">
<mat-error *ngIf="singleForm.get('autoCompleteOptions').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field>
<mat-checkbox class="col-12" [formControl]="singleForm.get('hasAuth')">{{'DATASET-PROFILE-EDITOR.STEPS.FORM.FIELD.FIELDS.FIELD-AUTOCOMPLETE-HAS-AUTH' | translate}}</mat-checkbox>
<div *ngIf="singleForm.get('hasAuth').value === true" class="col-12">
<mat-form-field class="col-md-6">
<mat-label>{{'DATASET-PROFILE-EDITOR.STEPS.FORM.FIELD.FIELDS.FIELD-AUTOCOMPLETE-AUTH-METHOD' | translate}}</mat-label>
<mat-select [formControl]="singleForm.get('auth').get('method')">
<mat-option *ngFor="let method of htmlMethods | keyvalue" [value]="method.value">{{method.value}}</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field class="col-md-6">
<mat-label>{{'DATASET-PROFILE-EDITOR.STEPS.FORM.FIELD.FIELDS.FIELD-AUTOCOMPLETE-URL' | translate}}</mat-label>
<input matInput [formControl]="singleForm.get('auth').get('url')">
<mat-error *ngIf="singleForm.get('auth').get('url').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field>
<mat-form-field class="col-md-6">
<mat-label>{{'DATASET-PROFILE-EDITOR.STEPS.FORM.FIELD.FIELDS.FIELD-AUTOCOMPLETE-AUTH-TYPE' | translate}}</mat-label>
<mat-select [formControl]="singleForm.get('auth').get('type')">
<mat-option *ngFor="let type of authTypes | keyvalue" [value]="type.value">{{type.value}}</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field class="col-md-6">
<mat-label>{{'DATASET-PROFILE-EDITOR.STEPS.FORM.FIELD.FIELDS.FIELD-AUTOCOMPLETE-OPTIONS-ROOT' | translate}}</mat-label>
<input matInput [formControl]="singleForm.get('auth').get('path')">
<mat-error *ngIf="singleForm.get('auth').get('path').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field>
<mat-form-field class="col-md-12">
<mat-label>{{'DATASET-PROFILE-EDITOR.STEPS.FORM.FIELD.FIELDS.FIELD-AUTOCOMPLETE-AUTH-BODY' | translate}}</mat-label>
<textarea matInput [formControl]="singleForm.get('auth').get('body')"></textarea>
<mat-error *ngIf="singleForm.get('auth').get('body').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field>
</div>
<button mat-button type="button" (click)="removeSource(i)"><mat-icon>delete</mat-icon></button>
</div>

View File

@ -3,6 +3,8 @@ import { UntypedFormGroup, UntypedFormArray, AbstractControl } from '@angular/fo
import { DatasetProfileComboBoxType } from '../../../../../../../core/common/enum/dataset-profile-combo-box-type';
import { AutoCompleteFieldDataEditorModel } from '../../../../admin/field-data/auto-complete-field-data-editor-model';
import { AutoCompleteSingleDataEditorModel } from '@app/ui/admin/dataset-profile/admin/field-data/auto-complete-single-data';
import { HtmlMethod } from '@app/core/model/dataset-profile-definition/html-method.enum';
import { AuthType } from '@app/core/model/dataset-profile-definition/auth-type.enum';
@Component({
selector: 'app-dataset-profile-editor-auto-complete-field-component',
@ -11,6 +13,9 @@ import { AutoCompleteSingleDataEditorModel } from '@app/ui/admin/dataset-profile
})
export class DatasetProfileEditorAutoCompleteFieldComponent implements OnInit {
public htmlMethods = HtmlMethod;
public authTypes = AuthType;
@Input() form: UntypedFormGroup;
private data: AutoCompleteFieldDataEditorModel = new AutoCompleteFieldDataEditorModel();
multiForm: UntypedFormArray;

View File

@ -457,6 +457,10 @@
"FIELD-AUTOCOMPLETE-SOURCE": "Source",
"FIELD-AUTOCOMPLETE-URL": "Url",
"FIELD-AUTOCOMPLETE-OPTIONS-ROOT": "Options Root",
"FIELD-AUTOCOMPLETE-HAS-AUTH": "Has Authentication",
"FIELD-AUTOCOMPLETE-AUTH-METHOD": "Method",
"FIELD-AUTOCOMPLETE-AUTH-BODY": "Request Body",
"FIELD-AUTOCOMPLETE-AUTH-TYPE": "Authentication Type",
"FIELD-DATE-PICKER-TITLE": "Date Picker",
"FIELD-DATE-PICKER-PLACEHOLDER": "Input Placeholder Text",
"FIELD-DATE-PICKER-LABEL": "Label",

View File

@ -457,6 +457,10 @@
"FIELD-AUTOCOMPLETE-SOURCE": "Source",
"FIELD-AUTOCOMPLETE-URL": "Url",
"FIELD-AUTOCOMPLETE-OPTIONS-ROOT": "Options Root",
"FIELD-AUTOCOMPLETE-HAS-AUTH": "Has Authentication",
"FIELD-AUTOCOMPLETE-AUTH-METHOD": "Method",
"FIELD-AUTOCOMPLETE-AUTH-BODY": "Request Body",
"FIELD-AUTOCOMPLETE-AUTH-TYPE": "Authentication Type",
"FIELD-DATE-PICKER-TITLE": "Date Picker",
"FIELD-DATE-PICKER-PLACEHOLDER": "Input Placeholder Text",
"FIELD-DATE-PICKER-LABEL": "Label",

View File

@ -457,6 +457,10 @@
"FIELD-AUTOCOMPLETE-SOURCE": "Fuente",
"FIELD-AUTOCOMPLETE-URL": "Url",
"FIELD-AUTOCOMPLETE-OPTIONS-ROOT": "Optiones principales",
"FIELD-AUTOCOMPLETE-HAS-AUTH": "Has Authentication",
"FIELD-AUTOCOMPLETE-AUTH-METHOD": "Method",
"FIELD-AUTOCOMPLETE-AUTH-BODY": "Request Body",
"FIELD-AUTOCOMPLETE-AUTH-TYPE": "Authentication Type",
"FIELD-DATE-PICKER-TITLE": "Campo de entrada para fecha",
"FIELD-DATE-PICKER-PLACEHOLDER": "Marcador de entrada",
"FIELD-DATE-PICKER-LABEL": "Etiqueta",

View File

@ -457,6 +457,10 @@
"FIELD-AUTOCOMPLETE-SOURCE": "Πηγή",
"FIELD-AUTOCOMPLETE-URL": "Url",
"FIELD-AUTOCOMPLETE-OPTIONS-ROOT": "Βάση Επιλογών",
"FIELD-AUTOCOMPLETE-HAS-AUTH": "Has Authentication",
"FIELD-AUTOCOMPLETE-AUTH-METHOD": "Method",
"FIELD-AUTOCOMPLETE-AUTH-BODY": "Request Body",
"FIELD-AUTOCOMPLETE-AUTH-TYPE": "Authentication Type",
"FIELD-DATE-PICKER-TITLE": "Επιλογή Ημερομηνίας",
"FIELD-DATE-PICKER-PLACEHOLDER": "Τοποθέτηση placeholder",
"FIELD-DATE-PICKER-LABEL": "Ετικέτα",

View File

@ -457,6 +457,10 @@
"FIELD-AUTOCOMPLETE-SOURCE": "Izvor",
"FIELD-AUTOCOMPLETE-URL": "Url",
"FIELD-AUTOCOMPLETE-OPTIONS-ROOT": "Opcija",
"FIELD-AUTOCOMPLETE-HAS-AUTH": "Has Authentication",
"FIELD-AUTOCOMPLETE-AUTH-METHOD": "Method",
"FIELD-AUTOCOMPLETE-AUTH-BODY": "Request Body",
"FIELD-AUTOCOMPLETE-AUTH-TYPE": "Authentication Type",
"FIELD-DATE-PICKER-TITLE": "Izbor datuma",
"FIELD-DATE-PICKER-PLACEHOLDER": "Polje za unos",
"FIELD-DATE-PICKER-LABEL": "Oznaka",

View File

@ -457,6 +457,10 @@
"FIELD-AUTOCOMPLETE-SOURCE": "Źródło",
"FIELD-AUTOCOMPLETE-URL": "Url",
"FIELD-AUTOCOMPLETE-OPTIONS-ROOT": "$Opcje główne$",
"FIELD-AUTOCOMPLETE-HAS-AUTH": "Has Authentication",
"FIELD-AUTOCOMPLETE-AUTH-METHOD": "Method",
"FIELD-AUTOCOMPLETE-AUTH-BODY": "Request Body",
"FIELD-AUTOCOMPLETE-AUTH-TYPE": "Authentication Type",
"FIELD-DATE-PICKER-TITLE": "Wybierz datę",
"FIELD-DATE-PICKER-PLACEHOLDER": "Wprowadź tekst zastępczy",
"FIELD-DATE-PICKER-LABEL": "Etykieta",

View File

@ -457,6 +457,10 @@
"FIELD-AUTOCOMPLETE-SOURCE": "Fonte",
"FIELD-AUTOCOMPLETE-URL": "URL",
"FIELD-AUTOCOMPLETE-OPTIONS-ROOT": "Opções de Raiz",
"FIELD-AUTOCOMPLETE-HAS-AUTH": "Has Authentication",
"FIELD-AUTOCOMPLETE-AUTH-METHOD": "Method",
"FIELD-AUTOCOMPLETE-AUTH-BODY": "Request Body",
"FIELD-AUTOCOMPLETE-AUTH-TYPE": "Authentication Type",
"FIELD-DATE-PICKER-TITLE": "Escolha da Data",
"FIELD-DATE-PICKER-PLACEHOLDER": "Sugestão de Preenchimento",
"FIELD-DATE-PICKER-LABEL": "Etiqueta",

View File

@ -457,6 +457,10 @@
"FIELD-AUTOCOMPLETE-SOURCE": "Source",
"FIELD-AUTOCOMPLETE-URL": "Url",
"FIELD-AUTOCOMPLETE-OPTIONS-ROOT": "Options Root",
"FIELD-AUTOCOMPLETE-HAS-AUTH": "Has Authentication",
"FIELD-AUTOCOMPLETE-AUTH-METHOD": "Method",
"FIELD-AUTOCOMPLETE-AUTH-BODY": "Request Body",
"FIELD-AUTOCOMPLETE-AUTH-TYPE": "Authentication Type",
"FIELD-DATE-PICKER-TITLE": "Date Picker",
"FIELD-DATE-PICKER-PLACEHOLDER": "Input Placeholder",
"FIELD-DATE-PICKER-LABEL": "Label",

View File

@ -457,6 +457,10 @@
"FIELD-AUTOCOMPLETE-SOURCE": "Izvor",
"FIELD-AUTOCOMPLETE-URL": "Url",
"FIELD-AUTOCOMPLETE-OPTIONS-ROOT": "Opcija",
"FIELD-AUTOCOMPLETE-HAS-AUTH": "Has Authentication",
"FIELD-AUTOCOMPLETE-AUTH-METHOD": "Method",
"FIELD-AUTOCOMPLETE-AUTH-BODY": "Request Body",
"FIELD-AUTOCOMPLETE-AUTH-TYPE": "Authentication Type",
"FIELD-DATE-PICKER-TITLE": "Izbor datuma",
"FIELD-DATE-PICKER-PLACEHOLDER": "Polje za unos",
"FIELD-DATE-PICKER-LABEL": "Oznaka",

View File

@ -457,6 +457,10 @@
"FIELD-AUTOCOMPLETE-SOURCE": "Kaynak",
"FIELD-AUTOCOMPLETE-URL": "Url",
"FIELD-AUTOCOMPLETE-OPTIONS-ROOT": "Options Root",
"FIELD-AUTOCOMPLETE-HAS-AUTH": "Has Authentication",
"FIELD-AUTOCOMPLETE-AUTH-METHOD": "Method",
"FIELD-AUTOCOMPLETE-AUTH-BODY": "Request Body",
"FIELD-AUTOCOMPLETE-AUTH-TYPE": "Authentication Type",
"FIELD-DATE-PICKER-TITLE": "Tarih Seçici",
"FIELD-DATE-PICKER-PLACEHOLDER": "Input Placeholder Text",
"FIELD-DATE-PICKER-LABEL": "Etiket",