diff --git a/build.gradle b/build.gradle index 9dd53b0a2c46d6b2cec7cf056b61633a7f4f6eea..d501fb01b2424d06d35114748b54e062a5afa56f 100644 --- a/build.gradle +++ b/build.gradle @@ -36,7 +36,6 @@ plugins { id "com.palantir.git-version" version "${gitVersionVersion}" apply false id "com.star-zero.gradle.githook" version "${githooksVersion}" id "eclipse-wtp" - id "io.micronaut.application" version "${micronautPluginVersion}" id "jacoco" id "maven-publish" } diff --git a/gradle.properties b/gradle.properties index 8b9194ffdc924b15cc7dd4c401439a767c47cf5a..66bdffc1bb41b0bdb74b642a849d23e66ad99aa0 100644 --- a/gradle.properties +++ b/gradle.properties @@ -8,12 +8,13 @@ jacksonVersion = 2.9.0 junitVersion = 5.8.2 micronautVersion = 3.2.3 micronautRxVersion = 2.1.1 -micronautPluginVersion = 3.1.1 nodePluginVersion = 3.0.1 nodeVersion = 16.3.0 nshmpLibVersion = 1.1.1 -nshmpWsUtilsVersion = 0.3.7 +nshmpWsUtilsVersion = 0.3.9 +openApiVersion = 4.0.0 shadowVersion = 7.1.2 spotbugsVersion = 4.7.0 spotlessVersion = 6.0.4 -swaggerVersion = 2.2.0 +swaggerParserVersion = 2.0.30 +swaggerVersion = 2.1.7 diff --git a/gradle/dependencies.gradle b/gradle/dependencies.gradle index af19505cd1bef520ab22beb648c802e286e8c3b5..4e5fb88aee8c360e87aad8fc31d0706cac8b2c95 100644 --- a/gradle/dependencies.gradle +++ b/gradle/dependencies.gradle @@ -13,10 +13,10 @@ dependencies { implementation "com.amazonaws:aws-java-sdk-ec2:${awsEc2Version}" // Micronaut - annotationProcessor platform("io.micronaut:micronaut-bom") + annotationProcessor platform("io.micronaut:micronaut-bom:${micronautVersion}") annotationProcessor "io.micronaut:micronaut-inject-java" annotationProcessor "io.micronaut:micronaut-validation" - implementation platform("io.micronaut:micronaut-bom") + implementation platform("io.micronaut:micronaut-bom:${micronautVersion}") implementation "io.micronaut:micronaut-http-client" implementation "io.micronaut:micronaut-inject" implementation "io.micronaut:micronaut-validation" @@ -28,13 +28,15 @@ dependencies { runtimeOnly "ch.qos.logback:logback-classic" // Swagger - annotationProcessor("io.micronaut.openapi:micronaut-openapi") - implementation("io.swagger.core.v3:swagger-annotations") - implementation("io.swagger.core.v3:swagger-models") + annotationProcessor("io.micronaut.openapi:micronaut-openapi:${openApiVersion}") + implementation("io.micronaut.openapi:micronaut-openapi") + implementation("io.swagger.core.v3:swagger-annotations:${swaggerVersion}") + implementation("io.swagger.core.v3:swagger-models:${swaggerVersion}") + implementation("io.swagger.parser.v3:swagger-parser:${swaggerParserVersion}") // junit - testAnnotationProcessor "io.micronaut:micronaut-inject-java" - testImplementation platform("io.micronaut:micronaut-bom") + testAnnotationProcessor "io.micronaut:micronaut-inject-java:${micronautVersion}" + testImplementation platform("io.micronaut:micronaut-bom:${micronautVersion}") testImplementation "org.junit.jupiter:junit-jupiter:${junitVersion}" testImplementation "org.junit.jupiter:junit-jupiter-api" testImplementation "io.micronaut.test:micronaut-test-junit5" diff --git a/src/main/java/gov/usgs/earthquake/nshmp/www/ServicesUtil.java b/src/main/java/gov/usgs/earthquake/nshmp/www/ServicesUtil.java index 3bdcab696e214e4d4940244a3ad557070552a54b..35032bae952860beb17a42ff8cf7b0a5a814eb0f 100644 --- a/src/main/java/gov/usgs/earthquake/nshmp/www/ServicesUtil.java +++ b/src/main/java/gov/usgs/earthquake/nshmp/www/ServicesUtil.java @@ -57,6 +57,14 @@ public class ServicesUtil { longitude = query.longitude; latitude = query.latitude; } + + public double getLongitude() { + return longitude; + } + + public double getLatitude() { + return latitude; + } } public enum Key { diff --git a/src/main/java/gov/usgs/earthquake/nshmp/www/ServletUtil.java b/src/main/java/gov/usgs/earthquake/nshmp/www/ServletUtil.java index 4896b9c81f8cc55e173db2bfe2b0b29a8cff94a6..8eaadb3536dc61b8c90d66af93bed58b041eb2ad 100644 --- a/src/main/java/gov/usgs/earthquake/nshmp/www/ServletUtil.java +++ b/src/main/java/gov/usgs/earthquake/nshmp/www/ServletUtil.java @@ -184,7 +184,7 @@ public class ServletUtil { return imt.toString(); } - public static Object serverData(int threads, Stopwatch timer) { + public static Server serverData(int threads, Stopwatch timer) { return new Server(threads, timer); } @@ -198,7 +198,7 @@ public class ServletUtil { return builder.build(); } - private static class Server { + public static class Server { final int threads; final String timer; @@ -209,5 +209,17 @@ public class ServletUtil { this.timer = timer.toString(); this.version = new HazVersion().getVersionInfo().version; } + + public int getThreads() { + return threads; + } + + public String getTimer() { + return timer; + } + + public String getVersion() { + return version; + } } } diff --git a/src/main/java/gov/usgs/earthquake/nshmp/www/SwaggerController.java b/src/main/java/gov/usgs/earthquake/nshmp/www/SwaggerController.java deleted file mode 100644 index dc4d6fb3cdf414424b09634b25c7d34bdd67f97c..0000000000000000000000000000000000000000 --- a/src/main/java/gov/usgs/earthquake/nshmp/www/SwaggerController.java +++ /dev/null @@ -1,48 +0,0 @@ -package gov.usgs.earthquake.nshmp.www; - -import java.nio.charset.StandardCharsets; -import java.util.stream.Collectors; - -import org.slf4j.LoggerFactory; - -import com.google.common.io.Resources; - -import io.micronaut.http.HttpRequest; -import io.micronaut.http.HttpResponse; -import io.micronaut.http.MediaType; -import io.micronaut.http.annotation.Controller; -import io.micronaut.http.annotation.Get; -import io.swagger.v3.oas.annotations.Hidden; -import io.swagger.v3.oas.annotations.tags.Tag; -import jakarta.inject.Inject; - -/** - * Expose OpenAPI YAML file. - * - * @author U.S. Geological Survey - */ -@Tag( - name = "Swagger", - description = "Swagger OpenAPI YAML") -@Hidden -@Controller("/swagger") -public class SwaggerController { - - @Inject - private NshmpMicronautServlet servlet; - - @Get(produces = MediaType.TEXT_EVENT_STREAM) - public HttpResponse<String> doGet(HttpRequest<?> request) { - try { - var url = Resources.getResource("META-INF/swagger/nshmp-haz.yml"); - var yml = Resources.readLines(url, StandardCharsets.UTF_8) - .stream() - .collect(Collectors.joining("\n")); - return HttpResponse.ok(yml); - } catch (Exception e) { - return ServletUtil.error( - LoggerFactory.getLogger("Swagger"), - e, "Swagger", request.getUri().toString()); - } - } -} diff --git a/src/main/java/gov/usgs/earthquake/nshmp/www/hazard/DisaggController.java b/src/main/java/gov/usgs/earthquake/nshmp/www/hazard/DisaggController.java index 5d55dc9a93ace2c69a4771b3ee972b0ba985ddf2..cedda9cb3037f726b07909c60303aa7fb8f1cf82 100644 --- a/src/main/java/gov/usgs/earthquake/nshmp/www/hazard/DisaggController.java +++ b/src/main/java/gov/usgs/earthquake/nshmp/www/hazard/DisaggController.java @@ -8,9 +8,13 @@ import java.util.Set; import gov.usgs.earthquake.nshmp.calc.DataType; import gov.usgs.earthquake.nshmp.gmm.Imt; import gov.usgs.earthquake.nshmp.www.NshmpMicronautServlet; +import gov.usgs.earthquake.nshmp.www.ResponseBody; import gov.usgs.earthquake.nshmp.www.ServletUtil; import gov.usgs.earthquake.nshmp.www.hazard.DisaggService.DisaggDataType; -import gov.usgs.earthquake.nshmp.www.hazard.HazardService.HazardImt; +import gov.usgs.earthquake.nshmp.www.hazard.DisaggService.RequestIml; +import gov.usgs.earthquake.nshmp.www.hazard.DisaggService.RequestRp; +import gov.usgs.earthquake.nshmp.www.hazard.DisaggService.Response; +import gov.usgs.earthquake.nshmp.www.hazard.HazardService.Metadata; import io.micronaut.core.annotation.Nullable; import io.micronaut.http.HttpRequest; @@ -21,6 +25,7 @@ import io.micronaut.http.annotation.Get; import io.micronaut.http.annotation.PathVariable; import io.micronaut.http.annotation.QueryValue; import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.tags.Tag; @@ -43,10 +48,13 @@ public class DisaggController { @Operation( summary = "Disaggregation model and service metadata", - description = "Returns details of the installed model and service request parameters") + description = "Returns details of the installed model and service request parameters", + operationId = "disagg-metadata") @ApiResponse( description = "Disaggregation service metadata", - responseCode = "200") + responseCode = "200", + content = @Content( + schema = @Schema(implementation = MetadataResponse.class))) @Get(produces = MediaType.APPLICATION_JSON) public HttpResponse<String> doGetMetadata(HttpRequest<?> http) { try { @@ -75,7 +83,9 @@ public class DisaggController { description = "Returns a hazard disaggregation computed from the installed model") @ApiResponse( description = "Disaggregation", - responseCode = "200") + responseCode = "200", + content = @Content( + schema = @Schema(implementation = DisaggResponseReturnPeriod.class))) @Get( uri = "{longitude}/{latitude}/{vs30}/{returnPeriod}{?imt}", produces = MediaType.APPLICATION_JSON) @@ -93,16 +103,15 @@ public class DisaggController { @Schema( minimum = "150", maximum = "3000") @PathVariable double returnPeriod, - @QueryValue @Nullable Set<HazardImt> imt, + @QueryValue @Nullable Set<Imt> imt, @QueryValue @Nullable Set<DisaggDataType> out) { try { Set<Imt> imts = HazardService.readImts(http); Set<DataType> dataTypes = HazardService.readDataTypes(http); DisaggService.RequestRp request = new DisaggService.RequestRp( http, - longitude, latitude, vs30, + longitude, latitude, vs30, imts, returnPeriod, - imts, dataTypes); return DisaggService.getDisaggRp(request); } catch (Exception e) { @@ -124,7 +133,9 @@ public class DisaggController { description = "Returns a hazard disaggregation computed from the installed model") @ApiResponse( description = "Disaggregation", - responseCode = "200") + responseCode = "200", + content = @Content( + schema = @Schema(implementation = DisaggResponseIml.class))) @Get( uri = "{longitude}/{latitude}/{vs30}", produces = MediaType.APPLICATION_JSON) @@ -139,20 +150,9 @@ public class DisaggController { @Schema( minimum = "150", maximum = "3000") @PathVariable double vs30, - @QueryValue @Nullable Double PGA, - @QueryValue @Nullable Double SA0P2, - @QueryValue @Nullable Double SA1P0, - @QueryValue @Nullable Double SA5P0, + @Schema( + example = "{\"PGA\": 0, \"SA0P2\": 0, \"SA1P0\": 0, \"SA2P0\": 0}") @QueryValue @Nullable Map<Imt, Double> imls, @QueryValue @Nullable Set<DisaggDataType> out) { - - /* - * Developer notes: - * - * It is awkward to support IMT=#; numerous unique keys that may or may not - * be present yields a clunky swagger interface. The disagg-iml endpoint - * requires one or more IMT=# query arguments. Document in example. - */ - try { Map<Imt, Double> imtImlMap = http.getParameters().asMap(Imt.class, Double.class); checkArgument(!imtImlMap.isEmpty(), "No IMLs supplied"); @@ -170,4 +170,13 @@ public class DisaggController { http.getUri().toString()); } } + + // For Swagger schema + private static class DisaggResponseIml extends ResponseBody<RequestIml, Response> {} + + // For Swagger schema + private static class DisaggResponseReturnPeriod extends ResponseBody<RequestRp, Response> {} + + // For Swagger schema + private static class MetadataResponse extends ResponseBody<String, Metadata> {}; } diff --git a/src/main/java/gov/usgs/earthquake/nshmp/www/hazard/DisaggService.java b/src/main/java/gov/usgs/earthquake/nshmp/www/hazard/DisaggService.java index 8a41630eafe3eb942596e0562f42a12ce8026f0a..abd46fdc89ac8b523365d3dad427869ed14a5f76 100644 --- a/src/main/java/gov/usgs/earthquake/nshmp/www/hazard/DisaggService.java +++ b/src/main/java/gov/usgs/earthquake/nshmp/www/hazard/DisaggService.java @@ -32,6 +32,8 @@ import gov.usgs.earthquake.nshmp.www.HazVersion; import gov.usgs.earthquake.nshmp.www.ResponseBody; import gov.usgs.earthquake.nshmp.www.ResponseMetadata; import gov.usgs.earthquake.nshmp.www.ServletUtil; +import gov.usgs.earthquake.nshmp.www.ServletUtil.Server; +import gov.usgs.earthquake.nshmp.www.hazard.HazardService.HazardRequest; import gov.usgs.earthquake.nshmp.www.hazard.HazardService.Metadata; import gov.usgs.earthquake.nshmp.www.meta.Parameter; @@ -202,12 +204,7 @@ public final class DisaggService { return disagg; } - static final class RequestIml { - - final transient HttpRequest<?> http; - final double longitude; - final double latitude; - final double vs30; + static final class RequestIml extends HazardRequest { final Map<Imt, Double> imls; final Set<DataType> dataTypes; @@ -218,48 +215,57 @@ public final class DisaggService { double vs30, Map<Imt, Double> imls, Set<DataType> dataTypes) { + super(http, longitude, latitude, vs30); - this.http = http; - this.longitude = longitude; - this.latitude = latitude; - this.vs30 = vs30; this.imls = imls; this.dataTypes = dataTypes; } - } - static final class RequestRp { + public Map<Imt, Double> getImls() { + return imls; + } + + public Set<DataType> getDataTypes() { + return dataTypes; + } + } - final transient HttpRequest<?> http; - final double longitude; - final double latitude; - final double vs30; + static final class RequestRp extends HazardRequest { final double returnPeriod; - final Set<Imt> imts; final Set<DataType> dataTypes; + final Set<Imt> imts; RequestRp( HttpRequest<?> http, double longitude, double latitude, double vs30, - double returnPeriod, Set<Imt> imts, + double returnPeriod, Set<DataType> dataTypes) { + super(http, longitude, latitude, vs30); - this.http = http; - this.longitude = longitude; - this.latitude = latitude; - this.vs30 = vs30; this.returnPeriod = returnPeriod; + this.dataTypes = dataTypes; this.imts = imts.isEmpty() ? ServletUtil.model().config().hazard.imts : imts; - this.dataTypes = dataTypes; + } + + public double getReturnPeriod() { + return returnPeriod; + } + + public Set<DataType> getDataTypes() { + return dataTypes; + } + + public Set<Imt> getImts() { + return imts; } } - private static final class Response { + static final class Response { final Response.Metadata metadata; final List<ImtDisagg> disaggs; @@ -268,17 +274,46 @@ public final class DisaggService { this.disaggs = disaggs; } + public Response.Metadata getMetadata() { + return metadata; + } + + public List<ImtDisagg> getDisaggs() { + return disaggs; + } + private static final class Metadata { - final Object server; + final Server server; final String rlabel = "Closest Distance, rRup (km)"; final String mlabel = "Magnitude (Mw)"; final String εlabel = "% Contribution to Hazard"; final Object εbins; - Metadata(Object server, Object εbins) { + Metadata(Server server, Object εbins) { this.server = server; this.εbins = εbins; } + + public Server getServer() { + return server; + } + + public String getRLabel() { + return rlabel; + } + + public String getMLabel() { + return mlabel; + } + + public String getεLabel() { + return εlabel; + } + + public Object getεbins() { + return εbins; + } + } private static final class Builder { @@ -326,7 +361,7 @@ public final class DisaggService { dataTypes.contains(DISAGG_DATA)))) .collect(toList()); - Object server = ServletUtil.serverData(ServletUtil.THREAD_COUNT, timer); + var server = ServletUtil.serverData(ServletUtil.THREAD_COUNT, timer); return new Response( new Response.Metadata(server, disagg.εBins()), @@ -345,5 +380,13 @@ public final class DisaggService { imt.name()); this.data = data; } + + public Parameter getImt() { + return imt; + } + + public Object getData() { + return data; + } } } diff --git a/src/main/java/gov/usgs/earthquake/nshmp/www/hazard/HazardController.java b/src/main/java/gov/usgs/earthquake/nshmp/www/hazard/HazardController.java index 03bc22142d9da072cdd6991f2269d389f1981095..e0fc167011d762a5d1d40c599ac7a16d5d299753 100644 --- a/src/main/java/gov/usgs/earthquake/nshmp/www/hazard/HazardController.java +++ b/src/main/java/gov/usgs/earthquake/nshmp/www/hazard/HazardController.java @@ -4,8 +4,11 @@ import java.util.Set; import gov.usgs.earthquake.nshmp.gmm.Imt; import gov.usgs.earthquake.nshmp.www.NshmpMicronautServlet; +import gov.usgs.earthquake.nshmp.www.ResponseBody; import gov.usgs.earthquake.nshmp.www.ServletUtil; -import gov.usgs.earthquake.nshmp.www.hazard.HazardService.HazardImt; +import gov.usgs.earthquake.nshmp.www.hazard.HazardService.Metadata; +import gov.usgs.earthquake.nshmp.www.hazard.HazardService.Request; +import gov.usgs.earthquake.nshmp.www.hazard.HazardService.Response; import io.micronaut.core.annotation.Nullable; import io.micronaut.http.HttpRequest; @@ -16,6 +19,7 @@ import io.micronaut.http.annotation.Get; import io.micronaut.http.annotation.PathVariable; import io.micronaut.http.annotation.QueryValue; import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.tags.Tag; @@ -38,10 +42,13 @@ public class HazardController { @Operation( summary = "Hazard model and service metadata", - description = "Returns details of the installed model and service request parameters") + description = "Returns details of the installed model and service request parameters", + operationId = "hazard-metadata") @ApiResponse( description = "Hazard service metadata", - responseCode = "200") + responseCode = "200", + content = @Content( + schema = @Schema(implementation = MetadataResponse.class))) @Get(produces = MediaType.APPLICATION_JSON) public HttpResponse<String> doGetMetadata(HttpRequest<?> http) { try { @@ -63,14 +70,15 @@ public class HazardController { * @param imt Optional IMTs at which to compute hazard. If none are supplied, * then the supported set for the installed model is used. Responses * for numerous IMT's are quite large, on the order of MB. - * */ @Operation( summary = "Compute probabilisitic hazard at a site", description = "Returns hazard curves computed from the installed model") @ApiResponse( description = "Hazard curves", - responseCode = "200") + responseCode = "200", + content = @Content( + schema = @Schema(implementation = HazardResponse.class))) @Get( uri = "/{longitude}/{latitude}/{vs30}{?truncate,maxdir,imt}", produces = MediaType.APPLICATION_JSON) @@ -89,14 +97,14 @@ public class HazardController { defaultValue = "false") @Nullable Boolean truncate, @QueryValue( defaultValue = "false") @Nullable Boolean maxdir, - @QueryValue @Nullable Set<HazardImt> imt) { + @QueryValue @Nullable Set<Imt> imt) { try { Set<Imt> imts = HazardService.readImts(http); HazardService.Request request = new HazardService.Request( http, longitude, latitude, vs30, - truncate, maxdir, - imts); + imts, + truncate, maxdir); return HazardService.getHazard(request); } catch (Exception e) { return ServletUtil.error( @@ -105,4 +113,10 @@ public class HazardController { http.getUri().toString()); } } + + // For Swagger schemas + private static class HazardResponse extends ResponseBody<Request, Response> {} + + // For Swagger schemas + private static class MetadataResponse extends ResponseBody<String, Metadata> {}; } diff --git a/src/main/java/gov/usgs/earthquake/nshmp/www/hazard/HazardService.java b/src/main/java/gov/usgs/earthquake/nshmp/www/hazard/HazardService.java index 28a2f6100431f0172379d3abaf7f6d53f4789f46..3f06ebd44b15cc80a8d86eb2a1fbcff0c59165a5 100644 --- a/src/main/java/gov/usgs/earthquake/nshmp/www/hazard/HazardService.java +++ b/src/main/java/gov/usgs/earthquake/nshmp/www/hazard/HazardService.java @@ -38,6 +38,7 @@ import gov.usgs.earthquake.nshmp.www.HazVersion; import gov.usgs.earthquake.nshmp.www.ResponseBody; import gov.usgs.earthquake.nshmp.www.ResponseMetadata; import gov.usgs.earthquake.nshmp.www.ServletUtil; +import gov.usgs.earthquake.nshmp.www.ServletUtil.Server; import gov.usgs.earthquake.nshmp.www.meta.DoubleParameter; import gov.usgs.earthquake.nshmp.www.meta.Parameter; import gov.usgs.earthquake.nshmp.www.services.SourceServices.SourceModel; @@ -60,33 +61,6 @@ public final class HazardService { private static final String TOTAL_KEY = "Total"; - /* For Swagger selections; mprs + pgv */ - enum HazardImt { - PGA, - PGV, - SA0P01, - SA0P02, - SA0P03, - SA0P05, - SA0P075, - SA0P1, - SA0P15, - SA0P2, - SA0P25, - SA0P3, - SA0P4, - SA0P5, - SA0P75, - SA1P0, - SA1P5, - SA2P0, - SA3P0, - SA4P0, - SA5P0, - SA7P5, - SA10P0; - } - /** HazardController.doGetUsage() handler. */ public static HttpResponse<String> getMetadata(HttpRequest<?> request) { var url = request.getUri().toString(); @@ -182,14 +156,55 @@ public final class HazardService { 150, 1500); } - } - static final class Request { + public SourceModel getModel() { + return model; + } + + public DoubleParameter getLongitude() { + return longitude; + } + + public DoubleParameter getLatitude() { + return latitude; + } + public DoubleParameter getVs30() { + return vs30; + } + } + + static class HazardRequest { final transient HttpRequest<?> http; final double longitude; final double latitude; final double vs30; + + public HazardRequest( + HttpRequest<?> http, + double longitude, + double latitude, + double vs30) { + this.http = http; + this.longitude = checkLongitude(longitude); + this.latitude = checkLatitude(latitude); + this.vs30 = checkInRange(Site.VS30_RANGE, Site.Key.VS30, vs30); + } + + public double getLongitude() { + return longitude; + } + + public double getLatitude() { + return latitude; + } + + public double getVs30() { + return vs30; + } + } + + static final class Request extends HazardRequest { final boolean truncate; final boolean maxdir; final Set<Imt> imts; @@ -198,24 +213,32 @@ public final class HazardService { HttpRequest<?> http, double longitude, double latitude, - int vs30, + double vs30, + Set<Imt> imts, boolean truncate, - boolean maxdir, - Set<Imt> imts) { - - this.http = http; - this.longitude = checkLongitude(longitude); - this.latitude = checkLatitude(latitude); - this.vs30 = checkInRange(Site.VS30_RANGE, Site.Key.VS30, vs30); + boolean maxdir) { + super(http, longitude, latitude, vs30); this.truncate = truncate; this.maxdir = maxdir; this.imts = imts.isEmpty() ? ServletUtil.model().config().hazard.imts : imts; } + + public boolean getTruncate() { + return truncate; + } + + public boolean getMaxdir() { + return maxdir; + } + + public Set<Imt> getImts() { + return imts; + } } - private static final class Response { + static class Response { final Metadata metadata; final List<ImtCurves> hazardCurves; @@ -225,14 +248,34 @@ public final class HazardService { this.hazardCurves = hazardCurves; } + public Metadata getMetadata() { + return metadata; + } + + public List<ImtCurves> getHazardCurves() { + return hazardCurves; + } + private static final class Metadata { - final Object server; + final Server server; final String xlabel = "Ground Motion (g)"; final String ylabel = "Annual Frequency of Exceedance"; - Metadata(Object server) { + Metadata(Server server) { this.server = server; } + + public Server getServer() { + return server; + } + + public String getXLabel() { + return xlabel; + } + + public String getYLabel() { + return ylabel; + } } private static final class Builder { @@ -305,7 +348,7 @@ public final class HazardService { hazards.add(new ImtCurves(imt, curves)); } - Object server = ServletUtil.serverData(ServletUtil.THREAD_COUNT, timer); + var server = ServletUtil.serverData(ServletUtil.THREAD_COUNT, timer); var response = new Response( new Response.Metadata(server), hazards); @@ -324,6 +367,14 @@ public final class HazardService { this.imt = new Parameter(ServletUtil.imtShortLabel(imt), imt.name()); this.data = data; } + + public Parameter getImt() { + return imt; + } + + public List<Curve> getData() { + return data; + } } private static final class Curve { @@ -334,6 +385,14 @@ public final class HazardService { this.component = component; this.values = values; } + + public String getComponent() { + return component; + } + + public XySequence getValues() { + return values; + } } private static final double TRUNCATION_LIMIT = 1e-4; diff --git a/src/main/java/gov/usgs/earthquake/nshmp/www/meta/DoubleParameter.java b/src/main/java/gov/usgs/earthquake/nshmp/www/meta/DoubleParameter.java index f38749853663d6a04568990e0c628a2f63e1058e..0f234031c6b59b40626187833c60156368c46faa 100644 --- a/src/main/java/gov/usgs/earthquake/nshmp/www/meta/DoubleParameter.java +++ b/src/main/java/gov/usgs/earthquake/nshmp/www/meta/DoubleParameter.java @@ -13,4 +13,20 @@ public final class DoubleParameter { this.min = min; this.max = max; } + + public String getName() { + return name; + } + + public String getUnits() { + return units; + } + + public double getMin() { + return min; + } + + public double getMax() { + return max; + } } diff --git a/src/main/java/gov/usgs/earthquake/nshmp/www/meta/Metadata.java b/src/main/java/gov/usgs/earthquake/nshmp/www/meta/Metadata.java index 0f2d40b1d6eeb39cd7d7de5d222542b0ef49f266..194b98e89003f8920513d3026edffd7a2166af2e 100644 --- a/src/main/java/gov/usgs/earthquake/nshmp/www/meta/Metadata.java +++ b/src/main/java/gov/usgs/earthquake/nshmp/www/meta/Metadata.java @@ -52,6 +52,14 @@ public final class Metadata { Coordinates.LAT_RANGE.lowerEndpoint(), Coordinates.LAT_RANGE.upperEndpoint()); } + + public DoubleParameter getLongitude() { + return longitude; + } + + public DoubleParameter getLatitude() { + return latitude; + } } public static String errorMessage(String url, Throwable e, boolean trace) { diff --git a/src/main/java/gov/usgs/earthquake/nshmp/www/meta/Parameter.java b/src/main/java/gov/usgs/earthquake/nshmp/www/meta/Parameter.java index c6463c3ba318bf035b232c09f845d3b8563854f7..60d8553752cdb4137bb8b7426e5f10e366f27544 100644 --- a/src/main/java/gov/usgs/earthquake/nshmp/www/meta/Parameter.java +++ b/src/main/java/gov/usgs/earthquake/nshmp/www/meta/Parameter.java @@ -9,4 +9,12 @@ public class Parameter { this.display = display; this.value = value; } + + public String getDisplay() { + return display; + } + + public String getValue() { + return value; + } } diff --git a/src/main/java/gov/usgs/earthquake/nshmp/www/RateController.java b/src/main/java/gov/usgs/earthquake/nshmp/www/services/RateController.java similarity index 75% rename from src/main/java/gov/usgs/earthquake/nshmp/www/RateController.java rename to src/main/java/gov/usgs/earthquake/nshmp/www/services/RateController.java index df15c1fdeb68712fe60169d4310be79e947482de..7bf98ecc6828ff574703446dc7319d4b2fa70c94 100644 --- a/src/main/java/gov/usgs/earthquake/nshmp/www/RateController.java +++ b/src/main/java/gov/usgs/earthquake/nshmp/www/services/RateController.java @@ -1,10 +1,16 @@ -package gov.usgs.earthquake.nshmp.www; +package gov.usgs.earthquake.nshmp.www.services; import java.util.Optional; -import gov.usgs.earthquake.nshmp.www.services.RateService; +import gov.usgs.earthquake.nshmp.www.NshmpMicronautServlet; +import gov.usgs.earthquake.nshmp.www.ResponseBody; +import gov.usgs.earthquake.nshmp.www.services.RateService.ProbabilityParameters; import gov.usgs.earthquake.nshmp.www.services.RateService.Query; +import gov.usgs.earthquake.nshmp.www.services.RateService.RateParameters; +import gov.usgs.earthquake.nshmp.www.services.RateService.RequestData; +import gov.usgs.earthquake.nshmp.www.services.RateService.ResponseData; import gov.usgs.earthquake.nshmp.www.services.RateService.Service; +import gov.usgs.earthquake.nshmp.www.services.RateService.Usage; import io.micronaut.core.annotation.Nullable; import io.micronaut.http.HttpRequest; @@ -15,6 +21,7 @@ import io.micronaut.http.annotation.Get; import io.micronaut.http.annotation.PathVariable; import io.micronaut.http.annotation.QueryValue; import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.responses.ApiResponse; import jakarta.inject.Inject; @@ -32,24 +39,6 @@ public class RateController { @Inject private NshmpMicronautServlet servlet; - /** - * GET method to return the usage information on the rate service. - * - * @param request The HTTP request - */ - @Operation( - summary = "Returns the earthquake rate service usage", - description = "Returns the supported:\n * Cutoff distance\n * Longitude\n * Latitude", - operationId = "rate_doGetUsageRate", - tags = { "Rate Service" }) - @ApiResponse( - description = "Earthquake rate usage", - responseCode = "200") - @Get(uri = "/rate/usage", produces = MediaType.APPLICATION_JSON) - public HttpResponse<String> doGetUsageRate(HttpRequest<?> request) { - return RateService.handleDoGetUsage(request, Service.RATE); - } - /** * GET method to compute annual-rate, query based. * @@ -64,8 +53,19 @@ public class RateController { operationId = "rate_doGetRate", tags = { "Rate Service" }) @ApiResponse( - description = "Earthquake annual-rates", - responseCode = "200") + description = "Earthquake annual-rates service metadata", + responseCode = "20x", + content = { + @Content( + schema = @Schema(implementation = RateMetadataResponse.class)) + }) + @ApiResponse( + description = "Earthquake annual-rates calculation response", + responseCode = "200", + content = { + @Content( + schema = @Schema(implementation = CalcResponse.class)) + }) @Get( uri = "/rate{?longitude,latitude,distance}", produces = MediaType.APPLICATION_JSON) @@ -102,8 +102,10 @@ public class RateController { operationId = "rate_doGetRateSlash", tags = { "Rate Service" }) @ApiResponse( - description = "Earthquake annual-rates", - responseCode = "200") + description = "Earthquake annual-rates calculation response", + responseCode = "200", + content = @Content( + schema = @Schema(implementation = CalcResponse.class))) @Get( uri = "/rate{/longitude}{/latitude}{/distance}", produces = MediaType.APPLICATION_JSON) @@ -126,25 +128,6 @@ public class RateController { return RateService.handleDoGetCalc(request, query); } - /** - * GET method to return the usage information on the probability service. - * - * @param request The HTTP request - */ - @Operation( - summary = "Returns the earthquake probability service usage", - description = "Returns the supported:\n " + - "* Timespan\n * Cutoff distance\n * Longitude\n * Latitude", - operationId = "probability_doGetProbabilityRate", - tags = { "Probability Service" }) - @ApiResponse( - description = "Earthquake probability usage", - responseCode = "200") - @Get(uri = "/probability/usage", produces = MediaType.APPLICATION_JSON) - public HttpResponse<String> doGetUsageProbability(HttpRequest<?> request) { - return RateService.handleDoGetUsage(request, Service.PROBABILITY); - } - /** * GET method to compute probability, query based. * @@ -160,8 +143,20 @@ public class RateController { operationId = "probability_doGetProbability", tags = { "Probability Service" }) @ApiResponse( - description = "Earthquake probabilities", - responseCode = "200") + description = "Earthquake probabilities service metadata", + responseCode = "20x", + content = { + @Content( + schema = @Schema(implementation = ProbMetadataResponse.class)) + }) + @ApiResponse( + description = "Earthquake probabilities calculation response", + responseCode = "200", + content = { + @Content( + schema = @Schema( + implementation = CalcResponse.class)) + }) @Get( uri = "/probability{?longitude,latitude,distance,timespan}", produces = MediaType.APPLICATION_JSON) @@ -203,8 +198,11 @@ public class RateController { operationId = "probability_doGetProbabilitySlash", tags = { "Probability Service" }) @ApiResponse( - description = "Earthquake probabilities", - responseCode = "200") + description = "Earthquake probabilities calculation response", + responseCode = "200", + content = @Content( + schema = @Schema( + implementation = CalcResponse.class))) @Get( uri = "/probability{/longitude}{/latitude}{/distance}{/timespan}", produces = MediaType.APPLICATION_JSON) @@ -231,4 +229,11 @@ public class RateController { return RateService.handleDoGetCalc(request, query); } + // Swagger schemas + private static class CalcResponse extends ResponseBody<RequestData, ResponseData> {} + + private static class RateMetadataResponse extends ResponseBody<String, Usage<RateParameters>> {}; + + private static class ProbMetadataResponse extends + ResponseBody<String, Usage<ProbabilityParameters>> {}; } diff --git a/src/main/java/gov/usgs/earthquake/nshmp/www/services/RateService.java b/src/main/java/gov/usgs/earthquake/nshmp/www/services/RateService.java index b4ce06c52b3314701941246fa723215577df9967..a084a6a0aa74963840b7f4697b54fd34a6f8b64b 100644 --- a/src/main/java/gov/usgs/earthquake/nshmp/www/services/RateService.java +++ b/src/main/java/gov/usgs/earthquake/nshmp/www/services/RateService.java @@ -19,13 +19,13 @@ import gov.usgs.earthquake.nshmp.calc.Site; import gov.usgs.earthquake.nshmp.geo.Location; import gov.usgs.earthquake.nshmp.model.HazardModel; import gov.usgs.earthquake.nshmp.www.HazVersion; -import gov.usgs.earthquake.nshmp.www.RateController; import gov.usgs.earthquake.nshmp.www.ResponseBody; import gov.usgs.earthquake.nshmp.www.ResponseMetadata; import gov.usgs.earthquake.nshmp.www.ServicesUtil.Key; import gov.usgs.earthquake.nshmp.www.ServicesUtil.ServiceQueryData; import gov.usgs.earthquake.nshmp.www.ServicesUtil.ServiceRequestData; import gov.usgs.earthquake.nshmp.www.ServletUtil; +import gov.usgs.earthquake.nshmp.www.ServletUtil.Server; import gov.usgs.earthquake.nshmp.www.WsUtils; import gov.usgs.earthquake.nshmp.www.meta.DoubleParameter; import gov.usgs.earthquake.nshmp.www.meta.Metadata.DefaultParameters; @@ -56,23 +56,6 @@ public final class RateService { private static final String TOTAL_KEY = "Total"; - /** - * Handler for {@link RateController#doGetUsageRate} and - * {@link RateController#doGetUsageProbability}. - * - * @param service The service - * @param urlHelper The url helper - */ - public static HttpResponse<String> handleDoGetUsage(HttpRequest<?> request, Service service) { - try { - var response = metadata(request, service); - var json = ServletUtil.GSON.toJson(response); - return HttpResponse.ok(json); - } catch (Exception e) { - return ServletUtil.error(LOG, e, service.name, request.getUri().getPath()); - } - } - /** * Handler for {@link RateController#doGetProbability}, * {@link RateController#doGetProbabilitySlash}, @@ -83,7 +66,7 @@ public final class RateService { * @param urlHelper The url helper * @return */ - public static HttpResponse<String> handleDoGetCalc(HttpRequest<?> request, Query query) { + static HttpResponse<String> handleDoGetCalc(HttpRequest<?> request, Query query) { var service = query.service; try { @@ -102,11 +85,12 @@ public final class RateService { } } - static ResponseBody<String, Usage> metadata(HttpRequest<?> request, Service service) { + static ResponseBody<String, Usage<DefaultParameters>> metadata(HttpRequest<?> request, + Service service) { var parameters = service == Service.RATE ? new RateParameters() : new ProbabilityParameters(); - var usage = new Usage(service, parameters); + var usage = new Usage<DefaultParameters>(service, parameters); var url = request.getUri().getPath(); - return ResponseBody.<String, Usage> usage() + return ResponseBody.<String, Usage<DefaultParameters>> usage() .name(service.name) .url(url) .metadata(new ResponseMetadata(HazVersion.appVersions())) @@ -167,6 +151,12 @@ public final class RateService { return ratesCombined; } + private static HttpResponse<String> handleDoGetUsage(HttpRequest<?> request, Service service) { + var response = metadata(request, service); + var json = ServletUtil.GSON.toJson(response); + return HttpResponse.ok(json); + } + private static ListenableFuture<EqRate> process( Service service, HazardModel model, @@ -251,6 +241,14 @@ public final class RateService { this.distance = query.distance; this.timespan = query.timespan; } + + public double getDistance() { + return distance; + } + + public Optional<Double> getTimespan() { + return timespan; + } } private static final class ServiceResponseMetadata { @@ -270,10 +268,34 @@ public final class RateService { this.ylabel = isProbability ? "Probability" : "Annual Rate (yrâ»Â¹)"; this.timespan = request.timespan.orElse(null); } + + public double getLatitude() { + return latitude; + } + + public double getLongitude() { + return longitude; + } + + public double getDistance() { + return distance; + } + + public Double getTimespan() { + return timespan; + } + + public String getXlabel() { + return xlabel; + } + + public String getYLabel() { + return ylabel; + } } - private static final class ResponseData { - final Object server; + static final class ResponseData { + final Server server; final ServiceResponseMetadata metadata; final List<Sequence> data; @@ -283,6 +305,18 @@ public final class RateService { this.data = buildSequence(rates); } + public Server getServer() { + return server; + } + + public ServiceResponseMetadata getMetadata() { + return metadata; + } + + public List<Sequence> getData() { + return data; + } + List<Sequence> buildSequence(EqRate rates) { var sequences = new ArrayList<Sequence>(); @@ -326,23 +360,45 @@ public final class RateService { this.xvalues = xvalues; this.yvalues = yvalues; } + + public String getComponent() { + return component; + } + + public List<Double> getXvalues() { + return xvalues; + } + + public List<Double> getYvalues() { + return yvalues; + } } - private static class Usage { + static class Usage<T extends DefaultParameters> { final String description; final List<String> syntax; - final Object server; - final DefaultParameters parameters; + final T parameters; - private Usage(Service service, DefaultParameters parameters) { + private Usage(Service service, T parameters) { description = service.description; this.syntax = service.syntax; - server = ServletUtil.serverData(1, Stopwatch.createStarted()); this.parameters = parameters; } + + public String getDescription() { + return description; + } + + public List<String> getSyntax() { + return syntax; + } + + public T getParameters() { + return parameters; + } } - private static class RateParameters extends DefaultParameters { + static class RateParameters extends DefaultParameters { final DoubleParameter distance; RateParameters() { @@ -353,9 +409,13 @@ public final class RateService { 0.01, 1000.0); } + + public DoubleParameter getDistance() { + return distance; + } } - private static class ProbabilityParameters extends RateParameters { + static class ProbabilityParameters extends RateParameters { final DoubleParameter timespan; ProbabilityParameters() { @@ -365,6 +425,9 @@ public final class RateService { Maths.TIMESPAN_RANGE.lowerEndpoint(), Maths.TIMESPAN_RANGE.upperEndpoint()); } - } + public DoubleParameter getTimespan() { + return timespan; + } + } } diff --git a/src/main/java/gov/usgs/earthquake/nshmp/www/SourceController.java b/src/main/java/gov/usgs/earthquake/nshmp/www/services/SourceController.java similarity index 71% rename from src/main/java/gov/usgs/earthquake/nshmp/www/SourceController.java rename to src/main/java/gov/usgs/earthquake/nshmp/www/services/SourceController.java index 3c86ffe988cd66edea5179ef4cc64be0d0bbc54f..b6fe870b5d8f6ddc6359971fcbda57f0b4a774c4 100644 --- a/src/main/java/gov/usgs/earthquake/nshmp/www/SourceController.java +++ b/src/main/java/gov/usgs/earthquake/nshmp/www/services/SourceController.java @@ -1,6 +1,8 @@ -package gov.usgs.earthquake.nshmp.www; +package gov.usgs.earthquake.nshmp.www.services; -import gov.usgs.earthquake.nshmp.www.services.SourceServices; +import gov.usgs.earthquake.nshmp.www.NshmpMicronautServlet; +import gov.usgs.earthquake.nshmp.www.ResponseBody; +import gov.usgs.earthquake.nshmp.www.services.SourceServices.ResponseData; import io.micronaut.http.HttpRequest; import io.micronaut.http.HttpResponse; @@ -8,6 +10,8 @@ import io.micronaut.http.MediaType; import io.micronaut.http.annotation.Controller; import io.micronaut.http.annotation.Get; import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.inject.Inject; @@ -43,10 +47,15 @@ public class SourceController { operationId = "source_doGetUsage") @ApiResponse( description = "Installed source model", - responseCode = "200") + responseCode = "200", + content = @Content( + schema = @Schema( + implementation = MetadataResponse.class))) @Get(produces = MediaType.APPLICATION_JSON) public HttpResponse<String> doGetUsage(HttpRequest<?> request) { return SourceServices.handleDoGetUsage(request); } + // For Swagger schemas + private static class MetadataResponse extends ResponseBody<String, ResponseData> {} } diff --git a/src/main/java/gov/usgs/earthquake/nshmp/www/SourceLogicTreesController.java b/src/main/java/gov/usgs/earthquake/nshmp/www/services/SourceLogicTreesController.java similarity index 68% rename from src/main/java/gov/usgs/earthquake/nshmp/www/SourceLogicTreesController.java rename to src/main/java/gov/usgs/earthquake/nshmp/www/services/SourceLogicTreesController.java index 7fe44c003dfce73020bbea90b956299703103535..5cdb6d5107020db30b1f23a536101aa0c7652e36 100644 --- a/src/main/java/gov/usgs/earthquake/nshmp/www/SourceLogicTreesController.java +++ b/src/main/java/gov/usgs/earthquake/nshmp/www/services/SourceLogicTreesController.java @@ -1,6 +1,8 @@ -package gov.usgs.earthquake.nshmp.www; +package gov.usgs.earthquake.nshmp.www.services; -import gov.usgs.earthquake.nshmp.www.services.SourceLogicTreesService; +import gov.usgs.earthquake.nshmp.www.NshmpMicronautServlet; +import gov.usgs.earthquake.nshmp.www.ResponseBody; +import gov.usgs.earthquake.nshmp.www.services.SourceLogicTreesService.RequestData; import io.micronaut.http.HttpRequest; import io.micronaut.http.HttpResponse; @@ -8,6 +10,8 @@ import io.micronaut.http.annotation.Controller; import io.micronaut.http.annotation.Get; import io.micronaut.http.annotation.PathVariable; import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.inject.Inject; @@ -33,7 +37,10 @@ public class SourceLogicTreesController { summary = "Hazard model source logic trees") @ApiResponse( description = "Source logic trees metadata", - responseCode = "200") + responseCode = "200", + content = @Content( + schema = @Schema( + implementation = MetadataResponse.class))) @Get public HttpResponse<String> doGetMetadata(HttpRequest<?> request) { return SourceLogicTreesService.handleDoGetMetadata(request); @@ -48,9 +55,17 @@ public class SourceLogicTreesController { summary = "Get NSHM source logic tree") @ApiResponse( description = "NSHM source logic tree", - responseCode = "200") + responseCode = "200", + content = @Content( + schema = @Schema(implementation = TreeResponse.class))) @Get(uri = "/{id}") public HttpResponse<String> doGetTrees(HttpRequest<?> request, @PathVariable int id) { return SourceLogicTreesService.handleDoGetTrees(request, id); } + + // For Swagger schemas + private static class MetadataResponse extends ResponseBody<String, Object> {} + + // For Swagger schemas + private static class TreeResponse extends ResponseBody<RequestData, Object> {} } diff --git a/src/main/java/gov/usgs/earthquake/nshmp/www/services/SourceLogicTreesService.java b/src/main/java/gov/usgs/earthquake/nshmp/www/services/SourceLogicTreesService.java index 5525e39647022f1523a0969ba0201a94de5fd8bd..8dde682dc8e14cef7c9e075d9cecaf788e478a42 100644 --- a/src/main/java/gov/usgs/earthquake/nshmp/www/services/SourceLogicTreesService.java +++ b/src/main/java/gov/usgs/earthquake/nshmp/www/services/SourceLogicTreesService.java @@ -8,7 +8,6 @@ import gov.usgs.earthquake.nshmp.www.HazVersion; import gov.usgs.earthquake.nshmp.www.ResponseBody; import gov.usgs.earthquake.nshmp.www.ResponseMetadata; import gov.usgs.earthquake.nshmp.www.ServletUtil; -import gov.usgs.earthquake.nshmp.www.SourceLogicTreesController; import io.micronaut.http.HttpRequest; import io.micronaut.http.HttpResponse; @@ -65,11 +64,15 @@ public class SourceLogicTreesService { } } - private static class RequestData { - int id; + static class RequestData { + final int id; RequestData(int id) { this.id = id; } + + public int getId() { + return id; + } } } diff --git a/src/main/java/gov/usgs/earthquake/nshmp/www/services/SourceServices.java b/src/main/java/gov/usgs/earthquake/nshmp/www/services/SourceServices.java index f72a1546e820b09e6283116236b4f415edef8823..525ed76b346e7485281ebdfe972049f4627a4d87 100644 --- a/src/main/java/gov/usgs/earthquake/nshmp/www/services/SourceServices.java +++ b/src/main/java/gov/usgs/earthquake/nshmp/www/services/SourceServices.java @@ -9,7 +9,6 @@ import java.util.Set; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.google.common.base.Stopwatch; import com.google.gson.Gson; import com.google.gson.GsonBuilder; @@ -38,9 +37,6 @@ import jakarta.inject.Singleton; public class SourceServices { private static final String NAME = "Source Model"; - private static final String DESCRIPTION = "Installed source model listing"; - private static final String SERVICE_DESCRIPTION = - "Utilities for querying earthquake source models"; static final Logger LOG = LoggerFactory.getLogger(RateService.class); @@ -55,7 +51,7 @@ public class SourceServices { .create(); } - public static HttpResponse<String> handleDoGetUsage(HttpRequest<?> request) { + static HttpResponse<String> handleDoGetUsage(HttpRequest<?> request) { var url = request.getUri().getPath(); try { var response = ResponseBody.usage() @@ -72,27 +68,29 @@ public class SourceServices { } } - /* - * task... service metadata should be in same package as services (why - * ResponseData is currently public); rename meta package to - */ - public static class ResponseData { + static class ResponseData { final String description; - final Object server; + final SourceModel model; public ResponseData() { - this.description = "Installed source model listing"; - this.server = ServletUtil.serverData( - ServletUtil.THREAD_COUNT, - Stopwatch.createStarted()); + description = "Installed source model listing"; + model = new SourceModel(ServletUtil.model()); + } + + public String getDescription() { + return description; + } + + public SourceModel getSourceModel() { + return model; } } public static class SourceModel { - String name; - Set<Gmm> gmms; - Map<NehrpSiteClass, Double> siteClasses; - List<Parameter> imts; + final String name; + final Set<Gmm> gmms; + final Map<NehrpSiteClass, Double> siteClasses; + final List<Parameter> imts; public SourceModel(HazardModel model) { name = model.name(); @@ -106,6 +104,22 @@ public class SourceServices { .map(imt -> new Parameter(ServletUtil.imtShortLabel(imt), imt.name())) .collect(toList()); } + + public String getName() { + return name; + } + + public Set<Gmm> getGmms() { + return gmms; + } + + public Map<NehrpSiteClass, Double> getSiteClasses() { + return siteClasses; + } + + public List<Parameter> getImts() { + return imts; + } } enum Attributes { diff --git a/src/main/java/gov/usgs/earthquake/nshmp/www/services/SwaggerController.java b/src/main/java/gov/usgs/earthquake/nshmp/www/services/SwaggerController.java new file mode 100644 index 0000000000000000000000000000000000000000..e9ce647c3411c820b41744c6169eca55c710aafd --- /dev/null +++ b/src/main/java/gov/usgs/earthquake/nshmp/www/services/SwaggerController.java @@ -0,0 +1,73 @@ +package gov.usgs.earthquake.nshmp.www.services; + +import java.util.List; +import java.util.stream.Collectors; + +import org.slf4j.LoggerFactory; + +import gov.usgs.earthquake.nshmp.model.HazardModel; +import gov.usgs.earthquake.nshmp.www.NshmpMicronautServlet; +import gov.usgs.earthquake.nshmp.www.ServletUtil; +import gov.usgs.earthquake.nshmp.www.SwaggerUtils; + +import io.micronaut.http.HttpRequest; +import io.micronaut.http.HttpResponse; +import io.micronaut.http.MediaType; +import io.micronaut.http.annotation.Controller; +import io.micronaut.http.annotation.Get; +import io.swagger.v3.core.util.Yaml; +import io.swagger.v3.oas.annotations.Hidden; +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.parser.OpenAPIV3Parser; +import jakarta.inject.Inject; + +/** + * Expose OpenAPI YAML file. + * + * @author U.S. Geological Survey + */ +@Tag( + name = "Swagger", + description = "Swagger OpenAPI YAML") +@Hidden +@Controller("/swagger") +public class SwaggerController { + + @Inject + private NshmpMicronautServlet servlet; + + @Get(produces = MediaType.TEXT_EVENT_STREAM) + public HttpResponse<String> doGet(HttpRequest<?> request) { + try { + var model = ServletUtil.model(); + var openApi = getOpenAPI(request, model); + return HttpResponse.ok(Yaml.pretty(openApi)); + } catch (Exception e) { + return ServletUtil.error( + LoggerFactory.getLogger("Swagger"), + e, "Swagger", request.getUri().toString()); + } + } + + private OpenAPI getOpenAPI( + HttpRequest<?> request, + HazardModel model) { + var openApi = new OpenAPIV3Parser().read("META-INF/swagger/nshmp-haz.yml"); + // TODO: Get min and max boundaries + // SwaggerUtils.addLocationBounds + var components = openApi.getComponents(); + var schemas = components.getSchemas(); + SwaggerUtils.siteClassSchema(schemas, List.copyOf(model.siteClasses().keySet())); + SwaggerUtils.imtSchema(schemas, + model.config().hazard.imts.stream().collect(Collectors.toList())); + openApi.servers(null); + + openApi.getInfo().setTitle(model.name() + " Web Services"); + openApi.getInfo().setDescription( + "National Seismic Hazard Model (NSHM) hazard calculations and queries for the " + + model.name() + " hazard model."); + + return openApi; + } +} diff --git a/src/main/resources/swagger/index.css b/src/main/resources/swagger/index.css index 65f90aca32b0c4145e7f2e7fa437cb2448e84b59..b514b935545bc9401d16f678674db2c91e44f4bb 100644 --- a/src/main/resources/swagger/index.css +++ b/src/main/resources/swagger/index.css @@ -12,7 +12,7 @@ body { } #swagger-ui { - padding-top: 1em; + padding-top: 2em; padding-bottom: 50px; } @@ -71,3 +71,66 @@ body { .nshmp-template-footer a:visited { color: white; } + +/* ---- Disclaimer ---- */ + +.wrapper { + box-sizing: border-box; + margin: 0 auto; + max-width: 1460px; + padding: 0 20px; + width: 100%; +} + +@media only screen and (max-width: 1000px) { + .alert { + line-height: 1.1 !important; + font-size: 10px !important; + } +} + +.alert { + background-color: #fff3cd; + border-color: #ffecb5; + box-shadow: 0 3px 1px -2px #0003, 0 2px 2px #00000024, 0 1px 5px #0000001f; + line-height: 1.5; + margin: 1em 1em 0 1em; + overflow: hidden; + padding: 1.5em 2em; + position: relative; + top: 50px; +} + +.alert:before { + content: '\0e002'; + font-family: 'Material Icons'; + font-size: 4.5em; + left: -.2em; + line-height: 1; + opacity: .075; + position: absolute; + top: -.2em; +} + +details { + cursor: pointer; + box-shadow: 0 3px 1px -2px #0003, 0 2px 2px #00000024, 0 1px 5px #0000001f; +} + +details:hover { + background-color: #f3f3f3; +} + +details[open] { + background-color: initial; + padding: 0 1.5em 1.5em 1.5em; +} + +summary { + font-weight: bold; + padding: 1.5em; +} + +details[open] > summary { + margin-left: -1.5em; +} diff --git a/src/main/resources/swagger/index.html b/src/main/resources/swagger/index.html index 9abcef98e83f25ea79343e6b65a74aa49b80c690..168a705708fd193226accb25175b23d2fce88467 100644 --- a/src/main/resources/swagger/index.html +++ b/src/main/resources/swagger/index.html @@ -18,12 +18,11 @@ <link rel="stylesheet" type="text/css" - href="https://unpkg.com/swagger-ui-themes@3.0.0/themes/3.x/theme-material.css" + href="index.css" /> <link rel="stylesheet" - type="text/css" - href="index.css" + href="https://fonts.googleapis.com/icon?family=Material+Icons%7cMerriweather:400,400italic,700%7cSource+Sans+Pro:400,300,700" /> </head> @@ -36,17 +35,29 @@ </div> </header> + <div class="wrapper"> + <div class="alert" role="alert"> + This software is preliminary or provisional and is subject to revision. + It is being provided to meet the need for timely best science. + The software has not received final approval by the U.S. Geological Survey (USGS). + No warranty, expressed or implied, is made by the USGS or the U.S. Government as to + the functionality of the software and related material nor shall the fact of release + constitute any such warranty. The software is provided on the condition that neither + the USGS nor the U.S. Government shall be held liable for any damages resulting from + the authorized or unauthorized use of the software. + </div> + </div> + <div id="swagger-ui"></div> <footer class="nshmp-template-footer"> - <a href="https://code.usgs.gov/ghsc/nshmp/nshmp-haz/-/blob/main/LICENSE.md"> + <a href="https://code.usgs.gov/ghsc/nshmp/nshmp-ws-static/-/blob/main/LICENSE.md"> License </a> - <a href="https://code.usgs.gov/ghsc/nshmp/nshmp-haz/-/blob/main/DISCLAIMER.md"> + <a href="https://code.usgs.gov/ghsc/nshmp/nshmp-ws-static/-/blob/main/DISCLAIMER.md"> Disclaimer </a> - </footer> <script src="index.js"></script> diff --git a/src/main/resources/swagger/index.js b/src/main/resources/swagger/index.js index 06e5a1326e3df94485c3d6879f3d3d773b4dcecf..443a1f6f88c3eabe9a95934c8c1678fd09568213 100644 --- a/src/main/resources/swagger/index.js +++ b/src/main/resources/swagger/index.js @@ -3,13 +3,17 @@ window.onload = function() { contextPath = contextPath.endsWith('/') ? contextPath.slice(0, -1) : contextPath; const ui = SwaggerUIBundle({ - url: `./swagger`, + defaultModelsExpandDepth: 0, + deepLinking: true, + // docExpansion: 'full', dom_id: '#swagger-ui', - tagsSorter: 'alpha', - presets: [SwaggerUIBundle.presets.apis, SwaggerUIStandalonePreset], + layout: 'BaseLayout', plugins: [SwaggerUIBundle.plugins.DownloadUrl, updateContextPath(contextPath)], + presets: [SwaggerUIBundle.presets.apis], + tagsSorter: 'alpha', + tryItOutEnabled: true, validatorUrl: null, - deepLinking: true + url: `./swagger`, }); window.ui = ui;