|
1 |
| -== Getting Started with the OGC API - Environmental Data Retrieval Candidate Standard |
2 |
| - |
3 |
| -TBA |
| 1 | +== Getting Started with the OGC API - Environmental Data Retrieval Draft Standard and Spring Boot |
4 | 2 |
|
5 | 3 | === Introduction
|
6 | 4 |
|
7 |
| -TBA |
| 5 | +This Getting Started guide introduces Java developers that use Spring to working with API definition files from the draft OGC API - Environmental Data Retrieval specification. The guide is inspired by the https://reflectoring.io/spring-boot-openapi/[API-First Development with Spring Boot and Swagger] tutorial on reflectoring.io. |
| 6 | + |
| 7 | + |
| 8 | +== Prerequisites |
| 9 | + |
| 10 | +* The Java Development Kit (JDK) should be installed on your machine. You can use either http://openjdk.java.net[OpenJDK] or https://www.oracle.com/java/technologies/javase-downloads.html[Oracle JDK]. |
| 11 | +* https://maven.apache.org/[Maven] should be available on your machine. |
| 12 | +* An Integrated Development Environment (IDE). Some popular ones include https://www.eclipse.org[Eclipse], https://netbeans.org[Apache Netbeans], https://code.visualstudio.com[Microsoft Visual Code] and https://www.jetbrains.com/idea/download/[IntelliJ IDEA Community edition]. |
| 13 | + |
| 14 | +[[step1]] |
| 15 | +== Step 1 - Creating a basic Spring boot application |
| 16 | + |
| 17 | +First create a basic spring boot application at http://start.spring.io |
| 18 | + |
| 19 | +Let's call the project SimpleEDRApi1. |
| 20 | + |
| 21 | +Ensure that you select Spring Web as a dependency. |
| 22 | + |
| 23 | +image::images/image1.png[width=601,height=389] |
| 24 | + |
| 25 | +Download the project by clicking on the GENERATE button. |
| 26 | + |
| 27 | +Notice that the project contains a class called `SimpleEDRApi1Application`. This is the Main Class for the application. |
| 28 | + |
| 29 | +Now configure the application to run to serve requests through port 8081 by adding `server.port=8081` to the file `/src/main/resources/application.properties`. In this tutorial we use port 8081, you could use any port you wish. |
| 30 | + |
| 31 | +[[step2]] |
| 32 | +== Step 2 - Adding the OpenAPITools Generator to the maven project |
| 33 | + |
| 34 | +Add the following to the pom.xml file. |
| 35 | + |
| 36 | +[source,xml] |
| 37 | +---- |
| 38 | +<plugin> |
| 39 | + <groupId>org.openapitools</groupId> |
| 40 | + <artifactId>openapi-generator-maven-plugin</artifactId> |
| 41 | + <version>4.2.3</version> |
| 42 | + <executions> |
| 43 | + <execution> |
| 44 | + <goals> |
| 45 | + <goal>generate</goal> |
| 46 | + </goals> |
| 47 | + <configuration> |
| 48 | + <inputSpec> |
| 49 | + ${project.basedir}/src/main/resources/openapi.yaml |
| 50 | + </inputSpec> |
| 51 | + <generatorName>spring</generatorName> |
| 52 | + <apiPackage>org.ogc.api</apiPackage> |
| 53 | + <modelPackage>org.ogc.model</modelPackage> |
| 54 | + <supportingFilesToGenerate> |
| 55 | + ApiUtil.java |
| 56 | + </supportingFilesToGenerate> |
| 57 | + <configOptions> |
| 58 | + <delegatePattern>true</delegatePattern> |
| 59 | + </configOptions> |
| 60 | + </configuration> |
| 61 | + </execution> |
| 62 | + </executions> |
| 63 | +</plugin> |
| 64 | +
|
| 65 | +---- |
| 66 | + |
| 67 | +Then unpackage the zip file https://github.com/opengeospatial/joint-ogc-osgeo-asf-sprint-2021/tree/master/docs/ogcapi-edr/openapi.zip[openapi.zip] file at `/src/main/resources/` in your maven project. This zip file contains the openapi definition document and related schema files. |
| 68 | + |
| 69 | + |
| 70 | +[[step3]] |
| 71 | +== Step 3 - Adding supporting libraries to the project |
| 72 | + |
| 73 | +We are next going to add a series of libraries to the pom.xml file. Some |
| 74 | +of those libraries have the same version number, so to make it easier to |
| 75 | +manage the version numbers we will add the version numbers to the |
| 76 | +pom.xml file as properties. This is shown below. |
| 77 | + |
| 78 | +[source,xml] |
| 79 | +---- |
| 80 | +<properties> |
| 81 | +<java.version>11</java.version> |
| 82 | +…Additional properties go here… |
| 83 | +</properties> |
| 84 | +---- |
| 85 | + |
| 86 | +So the properties should look as follows. |
| 87 | + |
| 88 | +[source,xml] |
| 89 | +---- |
| 90 | +<properties> |
| 91 | + <java.version>11</java.version> |
| 92 | + <swagger-annotations-version>1.5.22</swagger-annotations-version> |
| 93 | + <jackson-version>2.10.2</jackson-version> |
| 94 | + <jackson-databind-nullable>0.2.1</jackson-databind-nullable> |
| 95 | +</properties> |
| 96 | +---- |
| 97 | + |
| 98 | +You will need to add the following libraries. |
| 99 | + |
| 100 | +[source,xml] |
| 101 | +---- |
| 102 | +<dependency> |
| 103 | + <groupId>org.springframework.boot</groupId> |
| 104 | + <artifactId>spring-boot-starter-validation</artifactId> |
| 105 | +</dependency> |
| 106 | +<dependency> |
| 107 | + <groupId>io.swagger</groupId> |
| 108 | + <artifactId>swagger-annotations</artifactId> |
| 109 | + <version>${swagger-annotations-version}</version> |
| 110 | +</dependency> |
| 111 | +<dependency> |
| 112 | + <groupId>org.openapitools</groupId> |
| 113 | + <artifactId>jackson-databind-nullable</artifactId> |
| 114 | + <version>${jackson-databind-nullable}</version> |
| 115 | +</dependency> |
| 116 | +<dependency> |
| 117 | + <groupId>com.fasterxml.jackson.jaxrs</groupId> |
| 118 | + <artifactId>jackson-jaxrs-base</artifactId> |
| 119 | + <version>${jackson-version}</version> |
| 120 | +</dependency> |
| 121 | +<dependency> |
| 122 | + <groupId>com.fasterxml.jackson.core</groupId> |
| 123 | + <artifactId>jackson-core</artifactId> |
| 124 | + <version>${jackson-version}</version> |
| 125 | +</dependency> |
| 126 | +<dependency> |
| 127 | + <groupId>com.fasterxml.jackson.core</groupId> |
| 128 | + <artifactId>jackson-annotations</artifactId> |
| 129 | + <version>${jackson-version}</version> |
| 130 | +</dependency> |
| 131 | +<dependency> |
| 132 | + <groupId>com.fasterxml.jackson.core</groupId> |
| 133 | + <artifactId>jackson-databind</artifactId> |
| 134 | + <version>${jackson-version}</version> |
| 135 | +</dependency> |
| 136 | +<dependency> |
| 137 | + <groupId>com.fasterxml.jackson.jaxrs</groupId> |
| 138 | + <artifactId>jackson-jaxrs-json-provider</artifactId> |
| 139 | + <version>${jackson-version}</version> |
| 140 | +</dependency> |
| 141 | +<dependency> |
| 142 | + <groupId>com.fasterxml.jackson.datatype</groupId> |
| 143 | + <artifactId>jackson-datatype-joda</artifactId> |
| 144 | + <version>${jackson-version}</version> |
| 145 | +</dependency> |
| 146 | +---- |
| 147 | + |
| 148 | + |
| 149 | +[[step4]] |
| 150 | +== Step 4 - Building |
| 151 | + |
| 152 | +Now compile the OpenAPI definition by running the command: |
| 153 | + |
| 154 | +`$ mvn clean package` |
| 155 | + |
| 156 | +This will generate a stub of the API. Due to a current limitation of |
| 157 | +OpenAPITools Generator (see |
| 158 | +https://github.com/OpenAPITools/openapi-generator/issues/5381) the |
| 159 | +compilation fails to handle elements defined as ‘oneOf’ or 'allOf' options. So, you |
| 160 | +will see a compilation such as shown below. |
| 161 | + |
| 162 | +image::images/image2.png[width=447,height=314] |
| 163 | + |
| 164 | +As a workaround, create the following classes in the |
| 165 | +org.ogc.model package: |
| 166 | + |
| 167 | +* AnyOfobjectstring.java |
| 168 | +* OneOfobjectobject.java |
| 169 | +* OneOfpointGeoJSONmultipointGeoJSONlinestringGeoJSONmultilinestringGeoJSONpolygonGeoJSONmultipolygonGeoJSONgeometrycollectionGeoJSON.java |
| 170 | +* OneOfstringinteger.java |
| 171 | + |
| 172 | +The classes do not need to implement any methods in them. |
| 173 | + |
| 174 | +NOTE: These are the classes that the generated stubs are expecting...so ignore the unusual filenames. |
| 175 | + |
| 176 | +Now re-compile the OpenAPI definition by running the command: |
| 177 | + |
| 178 | +`$ mvn clean package` |
| 179 | + |
| 180 | +If successful, you should arrive at a `BUILD SUCCESS` message. |
| 181 | + |
| 182 | +image::images/image3.png[width=461,height=309] |
| 183 | + |
| 184 | +[[step5]] |
| 185 | +== Step 5 - Creating the Controller |
| 186 | + |
| 187 | +At this point, you now have the API and model stub that you will need to |
| 188 | +implement an interface that conforms to the OGC API - Environmental Data Retrieval Draft Standard. |
| 189 | + |
| 190 | +In the ‘target’ folder you will find a folder called ‘generated-sources’ |
| 191 | +that includes stubs for the API controllers and model. |
| 192 | + |
| 193 | +So we next override the methods provided by the API classes |
| 194 | +generated by the OpenAPITools Generator. Overriding these methods |
| 195 | +enables us to add business logic to those methods. |
| 196 | + |
| 197 | +So we create a class called `SimpleEdrApi1Controller` to sit alongside the |
| 198 | +`SimpleEdrApi1Application` that was created by the initializr on |
| 199 | +start.sprint.io in <<step1>>. |
| 200 | + |
| 201 | +Declare the `SimpleEdrApi1Controller` class as RestController as shown below. |
| 202 | + |
| 203 | +[source,java] |
| 204 | +---- |
| 205 | +@RestController |
| 206 | +public class SimpleEDRApi1Controller{ |
| 207 | +
|
| 208 | +
|
| 209 | +} |
| 210 | +---- |
| 211 | + |
| 212 | +Next we create a method inside `SimpleEdrApi1Controller` that is going to save us sometime when creating links. Copy the `createLink` method to the `SimpleEdrApi1Controller` class. |
| 213 | + |
| 214 | +[source,java] |
| 215 | +---- |
| 216 | +public Link createLink(String title, String rel, String type, String href) |
| 217 | +{ |
| 218 | + Link link = new Link(); |
| 219 | + link.setRel(rel); |
| 220 | + link.setType(type); |
| 221 | + link.setTitle(title); |
| 222 | + link.setHref(href); |
| 223 | +
|
| 224 | + return link; |
| 225 | +} |
| 226 | +---- |
| 227 | + |
| 228 | +At this point, we might as add a global variable for storing the URL of the endpoint. Declare this string at the global level. |
| 229 | + |
| 230 | +[source,java] |
| 231 | +---- |
| 232 | +String endpoint = "http://localhost:8081"; |
| 233 | +---- |
| 234 | + |
| 235 | +Next copy the stub of the `DefaultApi.getLandingPage` method into the |
| 236 | +`SimpleEdrApi1Controller` class. Then insert the following code inside the `SimpleEdrApi1Controller.getLandingPage` method. |
| 237 | + |
| 238 | +[source,java] |
| 239 | +---- |
| 240 | +LandingPage lp = new LandingPage(); |
| 241 | +lp.setTitle("OGC API - Environmental Data Retrieval tutorial"); |
| 242 | +lp.setDescription("An example of an implementation of OGC API - Environmental Data Retrieval using Spring.io"); |
| 243 | +
|
| 244 | +
|
| 245 | +lp.addLinksItem(createLink("this document","self", "application/json",endpoint+"?f=json")); |
| 246 | +lp.addLinksItem(createLink("OGC API conformance classes implemented by this server","conformance","application/json",endpoint+"/conformance")); |
| 247 | +lp.addLinksItem(createLink("Access the data","data","application/json",endpoint+"/collections")); |
| 248 | +
|
| 249 | +HttpHeaders headers = new HttpHeaders(); |
| 250 | +headers.add("Content-Type", "application/json"); |
| 251 | +ResponseEntity<LandingPage> re = new ResponseEntity<LandingPage>(lp, headers, HttpStatus.OK); |
| 252 | +---- |
| 253 | + |
| 254 | +Follow a similar approach of overriding the API methods generated by the OpenAPITools Generator. |
8 | 255 |
|
9 |
| -=== Basics |
| 256 | +[[step6]] |
| 257 | +== Step 6 - Running the application |
10 | 258 |
|
11 |
| -TBA |
| 259 | +Once you are through overriding the other API methods, run the Spring boot application with the following command. |
12 | 260 |
|
13 |
| -=== Advanced |
| 261 | +$ mvn spring-boot:run |
14 | 262 |
|
| 263 | +Now using a client application such as Postman, send a request to http://localhost:8081 to see the landing page. The response should be something like. |
15 | 264 |
|
16 |
| -TBA |
| 265 | +[source,json] |
| 266 | +---- |
| 267 | +{ |
| 268 | + "title": "OGC API - Environmental Data Retrieval using Spring.io tutorial", |
| 269 | + "description": "An example of an implementation of OGC API - Environmental Data Retrieval using Spring.io", |
| 270 | + "links": [ |
| 271 | + { |
| 272 | + "href": "http://localhost:8081?f=json", |
| 273 | + "rel": "self", |
| 274 | + "type": "application/json", |
| 275 | + "hreflang": null, |
| 276 | + "title": "this document", |
| 277 | + "length": null |
| 278 | + }, |
| 279 | + { |
| 280 | + "href": "http://localhost:8081/conformance", |
| 281 | + "rel": "conformance", |
| 282 | + "type": "application/json", |
| 283 | + "hreflang": null, |
| 284 | + "title": "OGC API conformance classes implemented by this server", |
| 285 | + "length": null |
| 286 | + }, |
| 287 | + { |
| 288 | + "href": "http://localhost:8081/collections", |
| 289 | + "rel": "data", |
| 290 | + "type": "application/json", |
| 291 | + "hreflang": null, |
| 292 | + "title": "Access the data", |
| 293 | + "length": null |
| 294 | + } |
| 295 | + ], |
| 296 | + "keywords": null, |
| 297 | + "provider": null, |
| 298 | + "contact": null |
| 299 | +} |
| 300 | +---- |
0 commit comments