some cleanup (#840)
* some cleanup * results from mvn spotless:apply * fix contructor --> constructor * revert change to fix CdsHooksServletIT * revert change to charts README.md * bump chart version, required due to changes in README.md
This commit is contained in:
@@ -45,7 +45,7 @@ public class Application extends SpringBootServletInitializer {
|
||||
|
||||
SpringApplication.run(Application.class, args);
|
||||
|
||||
// Server is now accessible at eg. http://localhost:8080/fhir/metadata
|
||||
// Server is now accessible at e.g. http://localhost:8080/fhir/metadata
|
||||
// UI is now accessible at http://localhost:8080/
|
||||
}
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ import org.springframework.beans.factory.annotation.Configurable;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Serial;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.opencds.cqf.fhir.cr.hapi.config.test.TestCdsHooksConfig.CDS_HOOKS_OBJECT_MAPPER_FACTORY;
|
||||
@@ -29,6 +30,8 @@ import static org.opencds.cqf.fhir.cr.hapi.config.test.TestCdsHooksConfig.CDS_HO
|
||||
@Configurable
|
||||
public class CdsHooksServlet extends HttpServlet {
|
||||
private static final Logger logger = LoggerFactory.getLogger(CdsHooksServlet.class);
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Autowired
|
||||
|
||||
@@ -179,9 +179,8 @@ public class ModuleConfigurationPrefetchSvc extends CdsPrefetchSvc {
|
||||
return true;
|
||||
}
|
||||
if (resource instanceof IBaseBundle) {
|
||||
return BundleUtil.toListOfEntries(fhirContext, (IBaseBundle) resource)
|
||||
.size()
|
||||
> 0;
|
||||
return !BundleUtil.toListOfEntries(fhirContext, (IBaseBundle) resource)
|
||||
.isEmpty();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -41,24 +41,28 @@ public class FhirServerConfigCommon {
|
||||
private static final Logger ourLog = LoggerFactory.getLogger(FhirServerConfigCommon.class);
|
||||
|
||||
public FhirServerConfigCommon(AppProperties appProperties) {
|
||||
ourLog.info("Server configured to " + (appProperties.getAllow_contains_searches() ? "allow" : "deny")
|
||||
+ " contains searches");
|
||||
ourLog.info("Server configured to " + (appProperties.getAllow_multiple_delete() ? "allow" : "deny")
|
||||
+ " multiple deletes");
|
||||
ourLog.info("Server configured to " + (appProperties.getAllow_external_references() ? "allow" : "deny")
|
||||
+ " external references");
|
||||
ourLog.info("Server configured to " + (appProperties.getDao_scheduling_enabled() ? "enable" : "disable")
|
||||
+ " DAO scheduling");
|
||||
ourLog.info("Server configured to " + (appProperties.getDelete_expunge_enabled() ? "enable" : "disable")
|
||||
+ " delete expunges");
|
||||
ourLog.info(
|
||||
"Server configured to " + (appProperties.getExpunge_enabled() ? "enable" : "disable") + " expunges");
|
||||
"Server configured to {} contains searches",
|
||||
appProperties.getAllow_contains_searches() ? "allow" : "deny");
|
||||
ourLog.info(
|
||||
"Server configured to " + (appProperties.getAllow_override_default_search_params() ? "allow" : "deny")
|
||||
+ " overriding default search params");
|
||||
ourLog.info("Server configured to "
|
||||
+ (appProperties.getAuto_create_placeholder_reference_targets() ? "allow" : "disable")
|
||||
+ " auto-creating placeholder references");
|
||||
"Server configured to {} multiple deletes",
|
||||
appProperties.getAllow_multiple_delete() ? "allow" : "deny");
|
||||
ourLog.info(
|
||||
"Server configured to {} external references",
|
||||
appProperties.getAllow_external_references() ? "allow" : "deny");
|
||||
ourLog.info(
|
||||
"Server configured to {} DAO scheduling",
|
||||
appProperties.getDao_scheduling_enabled() ? "enable" : "disable");
|
||||
ourLog.info(
|
||||
"Server configured to {} delete expunges",
|
||||
appProperties.getDelete_expunge_enabled() ? "enable" : "disable");
|
||||
ourLog.info("Server configured to {} expunges", appProperties.getExpunge_enabled() ? "enable" : "disable");
|
||||
ourLog.info(
|
||||
"Server configured to {} overriding default search params",
|
||||
appProperties.getAllow_override_default_search_params() ? "allow" : "deny");
|
||||
ourLog.info(
|
||||
"Server configured to {} auto-creating placeholder references",
|
||||
appProperties.getAuto_create_placeholder_reference_targets() ? "allow" : "disable");
|
||||
ourLog.info(
|
||||
"Server configured to auto-version references at paths {}",
|
||||
appProperties.getAuto_version_reference_at_paths());
|
||||
@@ -66,12 +70,14 @@ public class FhirServerConfigCommon {
|
||||
if (appProperties.getSubscription().getEmail() != null) {
|
||||
AppProperties.Subscription.Email email =
|
||||
appProperties.getSubscription().getEmail();
|
||||
ourLog.info("Server is configured to enable email with host '" + email.getHost() + "' and port "
|
||||
+ email.getPort());
|
||||
ourLog.info("Server will use '" + email.getFrom() + "' as the from email address");
|
||||
ourLog.info(
|
||||
"Server is configured to enable email with host '{}' and port {}",
|
||||
email.getHost(),
|
||||
email.getPort());
|
||||
ourLog.info("Server will use '{}' as the from email address", email.getFrom());
|
||||
|
||||
if (!Strings.isNullOrEmpty(email.getUsername())) {
|
||||
ourLog.info("Server is configured to use username '" + email.getUsername() + "' for email");
|
||||
ourLog.info("Server is configured to use username '{}' for email", email.getUsername());
|
||||
}
|
||||
|
||||
if (!Strings.isNullOrEmpty(email.getPassword())) {
|
||||
@@ -91,17 +97,19 @@ public class FhirServerConfigCommon {
|
||||
ourLog.info("Indexed on contained resource enabled");
|
||||
}
|
||||
|
||||
ourLog.info("Server configured to " + (appProperties.getPre_expand_value_sets() ? "enable" : "disable")
|
||||
+ " value set pre-expansion");
|
||||
ourLog.info(
|
||||
"Server configured to " + (appProperties.getEnable_task_pre_expand_value_sets() ? "enable" : "disable")
|
||||
+ " value set pre-expansion task");
|
||||
ourLog.info("Server configured for pre-expand value set default count of "
|
||||
+ (appProperties.getPre_expand_value_sets_default_count().toString()));
|
||||
ourLog.info("Server configured for pre-expand value set max count of "
|
||||
+ (appProperties.getPre_expand_value_sets_max_count().toString()));
|
||||
ourLog.info("Server configured for maximum expansion size of "
|
||||
+ (appProperties.getMaximum_expansion_size().toString()));
|
||||
"Server configured to {} value set pre-expansion",
|
||||
appProperties.getPre_expand_value_sets() ? "enable" : "disable");
|
||||
ourLog.info(
|
||||
"Server configured to {} value set pre-expansion task",
|
||||
appProperties.getEnable_task_pre_expand_value_sets() ? "enable" : "disable");
|
||||
ourLog.info(
|
||||
"Server configured for pre-expand value set default count of {}",
|
||||
appProperties.getPre_expand_value_sets_default_count());
|
||||
ourLog.info(
|
||||
"Server configured for pre-expand value set max count of {}",
|
||||
appProperties.getPre_expand_value_sets_max_count());
|
||||
ourLog.info("Server configured for maximum expansion size of {}", appProperties.getMaximum_expansion_size());
|
||||
}
|
||||
|
||||
@Bean
|
||||
@@ -194,8 +202,9 @@ public class FhirServerConfigCommon {
|
||||
|
||||
Integer maxFetchSize = appProperties.getMax_page_size();
|
||||
jpaStorageSettings.setFetchSizeDefaultMaximum(maxFetchSize);
|
||||
ourLog.info("Server configured to have a maximum fetch size of "
|
||||
+ (maxFetchSize == Integer.MAX_VALUE ? "'unlimited'" : maxFetchSize));
|
||||
ourLog.info(
|
||||
"Server configured to have a maximum fetch size of {}",
|
||||
maxFetchSize == Integer.MAX_VALUE ? "'unlimited'" : maxFetchSize);
|
||||
|
||||
Long reuseCachedSearchResultsMillis = appProperties.getReuse_cached_search_results_millis();
|
||||
jpaStorageSettings.setReuseCachedSearchResultsForMillis(reuseCachedSearchResultsMillis);
|
||||
@@ -237,19 +246,21 @@ public class FhirServerConfigCommon {
|
||||
// Set and/or recommend default Server ID Strategy of UUID when using the ANY Client ID Strategy
|
||||
if (appProperties.getClient_id_strategy() == JpaStorageSettings.ClientIdStrategyEnum.ANY) {
|
||||
if (appProperties.getServer_id_strategy() == null) {
|
||||
ourLog.info("Defaulting server to use '" + JpaStorageSettings.IdStrategyEnum.UUID
|
||||
+ "' Server ID Strategy when using the '" + JpaStorageSettings.ClientIdStrategyEnum.ANY
|
||||
+ "' Client ID Strategy");
|
||||
ourLog.info(
|
||||
"Defaulting server to use '{}' Server ID Strategy when using the '{}' Client ID Strategy",
|
||||
JpaStorageSettings.IdStrategyEnum.UUID,
|
||||
JpaStorageSettings.ClientIdStrategyEnum.ANY);
|
||||
appProperties.setServer_id_strategy(JpaStorageSettings.IdStrategyEnum.UUID);
|
||||
} else if (appProperties.getServer_id_strategy() != JpaStorageSettings.IdStrategyEnum.UUID) {
|
||||
ourLog.warn("WARNING: '" + JpaStorageSettings.IdStrategyEnum.UUID
|
||||
+ "' Server ID Strategy is highly recommended when using the '"
|
||||
+ JpaStorageSettings.ClientIdStrategyEnum.ANY + "' Client ID Strategy");
|
||||
ourLog.warn(
|
||||
"WARNING: '{}' Server ID Strategy is highly recommended when using the '{}' Client ID Strategy",
|
||||
JpaStorageSettings.IdStrategyEnum.UUID,
|
||||
JpaStorageSettings.ClientIdStrategyEnum.ANY);
|
||||
}
|
||||
}
|
||||
if (appProperties.getServer_id_strategy() != null) {
|
||||
jpaStorageSettings.setResourceServerIdStrategy(appProperties.getServer_id_strategy());
|
||||
ourLog.info("Server configured to use '" + appProperties.getServer_id_strategy() + "' Server ID Strategy");
|
||||
ourLog.info("Server configured to use '{}' Server ID Strategy", appProperties.getServer_id_strategy());
|
||||
}
|
||||
|
||||
// to Disable the Resource History
|
||||
|
||||
@@ -248,7 +248,7 @@ public class StarterJpaConfig {
|
||||
|
||||
List<String> allAllowedCORSOrigins = appProperties.getCors().getAllowed_origin();
|
||||
allAllowedCORSOrigins.forEach(config::addAllowedOriginPattern);
|
||||
ourLog.info("CORS allows the following origins: " + String.join(", ", allAllowedCORSOrigins));
|
||||
ourLog.info("CORS allows the following origins: {}", String.join(", ", allAllowedCORSOrigins));
|
||||
|
||||
config.addExposedHeader("Location");
|
||||
config.addExposedHeader("Content-Location");
|
||||
@@ -467,9 +467,7 @@ public class StarterJpaConfig {
|
||||
registerCustomInterceptors(fhirServer, appContext, appProperties.getCustomInterceptorClasses());
|
||||
|
||||
// register the IPS Provider
|
||||
if (!theIpsOperationProvider.isEmpty()) {
|
||||
fhirServer.registerProvider(theIpsOperationProvider.get());
|
||||
}
|
||||
theIpsOperationProvider.ifPresent(fhirServer::registerProvider);
|
||||
|
||||
if (appProperties.getUserRequestRetryVersionConflictsInterceptorEnabled()) {
|
||||
fhirServer.registerInterceptor(new UserRequestRetryVersionConflictsInterceptor());
|
||||
@@ -500,7 +498,7 @@ public class StarterJpaConfig {
|
||||
throw new ConfigurationException("Interceptor class was not found on classpath: " + className, e);
|
||||
}
|
||||
|
||||
// first check if the class a Bean in the app context
|
||||
// first check if the class is a Bean in the app context
|
||||
Object interceptor = null;
|
||||
try {
|
||||
interceptor = theAppContext.getBean(clazz);
|
||||
@@ -541,7 +539,7 @@ public class StarterJpaConfig {
|
||||
throw new ConfigurationException("Provider class was not found on classpath: " + className, e);
|
||||
}
|
||||
|
||||
// first check if the class a Bean in the app context
|
||||
// first check if the class is a Bean in the app context
|
||||
Object provider = null;
|
||||
try {
|
||||
provider = theAppContext.getBean(clazz);
|
||||
|
||||
@@ -50,11 +50,11 @@ public class RepositoryValidationInterceptorFactoryR4 implements IRepositoryVali
|
||||
IBundleProvider results = structureDefinitionResourceProvider.search(new SearchParameterMap()
|
||||
.setLoadSynchronous(true)
|
||||
.add(StructureDefinition.SP_KIND, new TokenParam("resource")));
|
||||
Map<String, List<StructureDefinition>> structureDefintions = results.getResources(0, results.size()).stream()
|
||||
Map<String, List<StructureDefinition>> structureDefinitions = results.getResources(0, results.size()).stream()
|
||||
.map(StructureDefinition.class::cast)
|
||||
.collect(Collectors.groupingBy(StructureDefinition::getType));
|
||||
|
||||
structureDefintions.forEach((key, value) -> {
|
||||
structureDefinitions.forEach((key, value) -> {
|
||||
String[] urls = value.stream().map(StructureDefinition::getUrl).toArray(String[]::new);
|
||||
repositoryValidatingRuleBuilder
|
||||
.forResourcesOfType(key)
|
||||
|
||||
@@ -50,11 +50,11 @@ public class RepositoryValidationInterceptorFactoryR4B implements IRepositoryVal
|
||||
IBundleProvider results = structureDefinitionResourceProvider.search(new SearchParameterMap()
|
||||
.setLoadSynchronous(true)
|
||||
.add(StructureDefinition.SP_KIND, new TokenParam("resource")));
|
||||
Map<String, List<StructureDefinition>> structureDefintions = results.getResources(0, results.size()).stream()
|
||||
Map<String, List<StructureDefinition>> structureDefinitions = results.getResources(0, results.size()).stream()
|
||||
.map(StructureDefinition.class::cast)
|
||||
.collect(Collectors.groupingBy(StructureDefinition::getType));
|
||||
|
||||
structureDefintions.forEach((key, value) -> {
|
||||
structureDefinitions.forEach((key, value) -> {
|
||||
String[] urls = value.stream().map(StructureDefinition::getUrl).toArray(String[]::new);
|
||||
repositoryValidatingRuleBuilder
|
||||
.forResourcesOfType(key)
|
||||
|
||||
@@ -49,11 +49,11 @@ public class RepositoryValidationInterceptorFactoryR5 implements IRepositoryVali
|
||||
IBundleProvider results = structureDefinitionResourceProvider.search(new SearchParameterMap()
|
||||
.setLoadSynchronous(true)
|
||||
.add(StructureDefinition.SP_KIND, new TokenParam("resource")));
|
||||
Map<String, List<StructureDefinition>> structureDefintions = results.getResources(0, results.size()).stream()
|
||||
Map<String, List<StructureDefinition>> structureDefinitions = results.getResources(0, results.size()).stream()
|
||||
.map(StructureDefinition.class::cast)
|
||||
.collect(Collectors.groupingBy(StructureDefinition::getType));
|
||||
|
||||
structureDefintions.forEach((key, value) -> {
|
||||
structureDefinitions.forEach((key, value) -> {
|
||||
String[] urls = value.stream().map(StructureDefinition::getUrl).toArray(String[]::new);
|
||||
repositoryValidatingRuleBuilder
|
||||
.forResourcesOfType(key)
|
||||
|
||||
@@ -11,7 +11,7 @@ public class PostInitProviderRegisterer {
|
||||
resourceProviderFactory.attach(new Observer(restfulServer));
|
||||
}
|
||||
|
||||
private class Observer implements IResourceProviderFactoryObserver {
|
||||
private static class Observer implements IResourceProviderFactoryObserver {
|
||||
private RestfulServer restfulServer;
|
||||
|
||||
public Observer(RestfulServer restfulServer) {
|
||||
|
||||
@@ -188,14 +188,12 @@ public class EnvironmentHelper {
|
||||
public static Map<String, Object> getAllProperties(PropertySource<?> aPropSource) {
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
|
||||
if (aPropSource instanceof CompositePropertySource) {
|
||||
CompositePropertySource cps = (CompositePropertySource) aPropSource;
|
||||
if (aPropSource instanceof CompositePropertySource cps) {
|
||||
cps.getPropertySources().forEach(ps -> addAll(result, getAllProperties(ps)));
|
||||
return result;
|
||||
}
|
||||
|
||||
if (aPropSource instanceof EnumerablePropertySource<?>) {
|
||||
EnumerablePropertySource<?> ps = (EnumerablePropertySource<?>) aPropSource;
|
||||
if (aPropSource instanceof EnumerablePropertySource<?> ps) {
|
||||
Arrays.asList(ps.getPropertyNames()).forEach(key -> result.put(key, ps.getProperty(key)));
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ server:
|
||||
tomcat:
|
||||
# allow | as a separator in the URL
|
||||
relaxed-query-chars: "|"
|
||||
#Adds the option to go to eg. http://localhost:8080/actuator/health for seeing the running configuration
|
||||
#Adds the option to go to e.g. http://localhost:8080/actuator/health for seeing the running configuration
|
||||
#see https://docs.spring.io/spring-boot/docs/current/reference/html/actuator.html#actuator.endpoints
|
||||
management:
|
||||
#The following configuration will enable the actuator endpoints at /actuator/health, /actuator/info, /actuator/prometheus, /actuator/metrics. For security purposes, only /actuator/health is enabled by default.
|
||||
@@ -14,7 +14,7 @@ management:
|
||||
enabled-by-default: false
|
||||
web:
|
||||
exposure:
|
||||
include: 'health' # or e.g. 'info,health,prometheus,metrics' or '*' for all'
|
||||
include: 'health' # or e.g. 'info,health,prometheus,metrics' or '*' for all
|
||||
endpoint:
|
||||
info:
|
||||
enabled: true
|
||||
@@ -293,12 +293,12 @@ hapi:
|
||||
|
||||
# comma-separated list of fully qualified interceptor classes.
|
||||
# classes listed here will be fetched from the Spring context when combined with 'custom-bean-packages',
|
||||
# or will be instantiated via reflection using an no-arg contructor; then registered with the server
|
||||
# or will be instantiated via reflection using a no-arg constructor; then registered with the server
|
||||
#custom-interceptor-classes:
|
||||
|
||||
# comma-separated list of fully qualified provider classes.
|
||||
# classes listed here will be fetched from the Spring context when combined with 'custom-bean-packages',
|
||||
# or will be instantiated via reflection using an no-arg contructor; then registered with the server
|
||||
# or will be instantiated via reflection using a no-arg constructor; then registered with the server
|
||||
#custom-provider-classes:
|
||||
# specify what should be stored in meta.source based on StoreMetaSourceInformationEnum defaults to NONE
|
||||
# store_meta_source_information: NONE
|
||||
|
||||
@@ -3,7 +3,7 @@ server:
|
||||
# servlet:
|
||||
# context-path: /example/path
|
||||
port: 8080
|
||||
#Adds the option to go to eg. http://localhost:8080/actuator/health for seeing the running configuration
|
||||
#Adds the option to go to e.g. http://localhost:8080/actuator/health for seeing the running configuration
|
||||
#see https://docs.spring.io/spring-boot/docs/current/reference/html/actuator.html#actuator.endpoints
|
||||
management:
|
||||
#The following configuration will enable the actuator endpoints at /actuator/health, /actuator/info, /actuator/prometheus, /actuator/metrics. For security purposes, only /actuator/health is enabled by default.
|
||||
@@ -11,7 +11,7 @@ management:
|
||||
enabled-by-default: false
|
||||
web:
|
||||
exposure:
|
||||
include: 'health' # or e.g. 'info,health,prometheus,metrics' or '*' for all'
|
||||
include: 'health' # or e.g. 'info,health,prometheus,metrics' or '*' for all
|
||||
endpoint:
|
||||
info:
|
||||
enabled: true
|
||||
@@ -149,7 +149,7 @@ hapi:
|
||||
### forces the use of the https:// protocol for the returned server address.
|
||||
### alternatively, it may be set using the X-Forwarded-Proto header.
|
||||
# use_apache_address_strategy_https: false
|
||||
### enables the server to overwrite defaults on HTML, css, etc. under the url pattern of eg. /content/custom **
|
||||
### enables the server to overwrite defaults on HTML, css, etc. under the url pattern of e.g. /content/custom **
|
||||
### Folder with custom content MUST be named custom. If omitted then default content applies
|
||||
#custom_content_path: ./custom
|
||||
### enables the server host custom content. If e.g. the value ./configs/app is supplied then the content
|
||||
@@ -198,7 +198,7 @@ hapi:
|
||||
# enable_index_of_type: true
|
||||
# enable_index_contained_resource: false
|
||||
# resource_dbhistory_enabled: false
|
||||
### !!Extended Lucene/Elasticsearch Indexing is still a experimental feature, expect some features (e.g. _total=accurate) to not work as expected!!
|
||||
### !!Extended Lucene/Elasticsearch Indexing is still an experimental feature, expect some features (e.g. _total=accurate) to not work as expected!!
|
||||
### more information here: https://hapifhir.io/hapi-fhir/docs/server_jpa/elastic.html
|
||||
advanced_lucene_indexing: false
|
||||
search_index_full_text_enabled: false
|
||||
@@ -257,12 +257,12 @@ hapi:
|
||||
|
||||
# comma-separated list of fully qualified interceptor classes.
|
||||
# classes listed here will be fetched from the Spring context when combined with 'custom-bean-packages',
|
||||
# or will be instantiated via reflection using an no-arg contructor; then registered with the server
|
||||
# or will be instantiated via reflection using a no-arg constructor; then registered with the server
|
||||
#custom-interceptor-classes:
|
||||
|
||||
# comma-separated list of fully qualified provider classes.
|
||||
# classes listed here will be fetched from the Spring context when combined with 'custom-bean-packages',
|
||||
# or will be instantiated via reflection using an no-arg contructor; then registered with the server
|
||||
# or will be instantiated via reflection using a no-arg constructor; then registered with the server
|
||||
#custom-provider-classes:
|
||||
|
||||
# Threadpool size for BATCH'ed GETs in a bundle.
|
||||
|
||||
@@ -47,7 +47,7 @@
|
||||
<p>
|
||||
This UI can be customized!
|
||||
You might want to put rules on who can use this server here, or
|
||||
notices about privacy, or whatever else you want..
|
||||
notices about privacy, or whatever else you want.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -63,7 +63,7 @@
|
||||
<script>
|
||||
// Function to check if a file exists using AJAX
|
||||
function fileExists(filePath, callback) {
|
||||
var xhr = new XMLHttpRequest();
|
||||
let xhr = new XMLHttpRequest();
|
||||
xhr.onreadystatechange = function() {
|
||||
if (xhr.readyState === 4) {
|
||||
callback(xhr.status === 200);
|
||||
@@ -77,7 +77,7 @@
|
||||
fileExists("content/custom/about.html", function(exists) {
|
||||
if (exists) {
|
||||
loadFile("content/custom/about.html", function(content) {
|
||||
var replacementContainer = document.getElementById("replacementAbout");
|
||||
let replacementContainer = document.getElementById("replacementAbout");
|
||||
replacementContainer.innerHTML = content;
|
||||
});
|
||||
}
|
||||
@@ -85,7 +85,7 @@
|
||||
|
||||
// Function to load file content using AJAX
|
||||
function loadFile(filePath, callback) {
|
||||
var xhr = new XMLHttpRequest();
|
||||
let xhr = new XMLHttpRequest();
|
||||
xhr.onreadystatechange = function() {
|
||||
if (xhr.readyState === 4 && xhr.status === 200) {
|
||||
callback(xhr.responseText);
|
||||
|
||||
Reference in New Issue
Block a user