Skip to content

Latest commit

 

History

History
462 lines (368 loc) · 19.1 KB

File metadata and controls

462 lines (368 loc) · 19.1 KB

Developer Lab 3 - Swagger to REST

In this lab, we will use Contract-first API development with a database interface, implemented using CodeReady Workspaces, based on Eclipse Che Open Source project.

Audience: API Owner, Product Manager, Developers, Architects

Overview

In the context of defining API’s, it’s common for a Business Analyst (or Citizen Integrator) to first create an API specification or contract. By beginning the process with a clearly defined contract, a Developer can easily take the contract and auto-generate the underlying service to implement that API. This separation of concern, whereby a Citizen Integrator and Developer can independently collaborate and contribute to create an end-to-end API implementation, is a powerful method for defining API’s.

Why Red Hat?

CodeReady Workspaces, our online IDE, provides important functionality for implementing API services. In this lab you can see how our API tooling fits together with 3scale, Fuse and OpenShift to create a scalable API.

Skipping The Lab

If you are planning to follow to the next lab or are having trouble with this lab, you can reference the working project here

Credentials:

Your username is: {user-username}
Your password is: openshift

CodeReady Workspaces
OpenShift Console

Create a CodeReady Workspaces environment

  1. Logon to CodeReady Workspaces with username {user-username} and password openshift.

    code-ready-login
  2. Notice a workspace dil-ai-{user-username} is created for you. Click on the workspace to see the details.

    code-ready-workspace
  3. Notice the workspace is in Running state.

    code-ready-workspace-details
  4. Click on the Actions dropdown, then Open to enter the workspace.

    code-ready-open
  5. After a couple of minutes, the workspace opens and a project is cloned from the git repository for the workshop. (Tip: You need to click Yes, I trust in the bottom right corner if prompted.)

    che-user-workspace-dashboard

Was the DILAgileIntegration project opened in the workspace?

Try to redo this section, if any problem persists have your instructor check the Kubernetes pod that contains the CodeReady Workspaces application.

Setup a sample database

  1. Open your OpenShift Console.

  2. Enter your credentials: {user-username} and openshift

  3. In the Administrator view, click on the Projects link, and choose {user-username} project.

    ocp-adm-project-view
  4. Choose the Developer view.

    ocp-view-developer
  5. Navigate your mouse pointer to the +Add link and click on Database.

    developer-view-add-database
  6. Select the PostgreSQL (Ephemeral) template.

    ocp-add-db-postgresql-template
  7. In the pop-up window that appears, click on Instantiate Template.

    ocp-add-db-postgresql-instantiate
  8. Update PostgreSQL Connection Username to dbuser and PostgreSQL Connection Password to password.

    ocp-db-postgresql-config
  9. Click Create to generate the service.

Did (DC) postgresql pod appear with a dark blue circle around it?

Try to redo this section, if any problem persists have your instructor check the Kubernetes pod that contains the CodeReady Workspaces application.

View the skeleton projects imported from Git

  1. In CodeReady Workspaces, notice that the projects are imported from your Gogs repository.

    che-dil-ai-projects
  2. There should be 3 projects imported:

    1. location-gateway

    2. location-service

    3. location-soap2rest

Were you able to view the projects in your workspace?

Try to redo this section, if any problem persists have your instructor check the Kubernetes pod that contains the CodeReady Workspaces application.

Import the OpenAPI specification

Once you’ve received the OpenAPI specification (API contract) from your friendly Citizen Integrator, we need to import it into our skeleton Maven project (location-service). Follow these steps:

  1. Expand the location-service project and right-click on the src folder, selecting New Folder.

    che-location-service-new-folder.png
  2. Give the folder the name spec.

    che-create-folder-spec
  3. Right-click on your newly created spec folder and select New File. Name the file location.yaml.

    che-new-file-location-yaml
  4. Copy and paste the contents of the following file into your newly created location.yaml: https://raw.githubusercontent.com/RedHat-Middleware-Workshops/dayinthelife-integration/master/docs/labs/developer-track/resources/Locations.yaml. (CodeReady Workspaces will auto-save the file.)

  5. Click TerminalOpen Terminal in specific containeropenshift-tools and notice that a new terminal window opens in the lower section of the workspace.

    che-terminal-openshift-tools
  6. We need to login to OpenShift via the Terminal. Navigate back to the OpenShift web UI, click the {user-username} menu, and select the Copy Login Command link.

    ocp-copy-login-command
  7. Login to OpenShift web console and click on Display Token to view the login token. Copy the oc login …​ command.

    ocp-client-token-copy
  8. Switch back to CodeReady Workspaces, and paste the oc login …​ command into the openshift-tools terminal that you opened previously.

  9. Once you’ve logged-in, select your OpenShift project using command below:

    oc project {user-username}
  10. In the terminal, type following command to change directory to the location-service

    cd location-service/
  11. Next, enter the Maven command to run the build:

    mvn camel-restdsl-openapi:generate
  12. If everything completes successfully, it should generate a new file under src/main/java/com/redhat called CamelRoutes.java. If the Maven script fails, it’s probably because you forgot to first change into the location-service project in the previous step. Be sure to do this and re-run the command to fix the error.

    che-mvn-generate-camelroutes

Were you able to generate the CamelRoutes.java file?

Try to redo this section, if any problem persists have your instructor check the Kubernetes pod that contains the CodeReady Workspaces application.

Update the Camel Routes

  1. Click on the workspace button (located next to the Manage Commands button). Open the CamelRoutes.java file under src/main/java/com/redhat. Notice that the camel-restdsl-swagger-plugin maven plugin has generated Camel RESTdsl code for the various HTTP GET and POST operations. What is missing though are the underlying Camel routes, which will form our API service implementations. If the CamelRoutes.java hasn’t appeared, please right-click on the location-service project and click Refresh to manually refresh the project tree. The following file should be generated:

     package com.redhat;
    
     import javax.annotation.Generated;
     import org.apache.camel.builder.RouteBuilder;
     import org.apache.camel.model.rest.RestParamType;
    
     /**
      * Generated from Swagger specification by Camel REST DSL generator.
      */
     @Generated("org.apache.camel.generator.swagger.PathGenerator")
     public final class CamelRoutes extends RouteBuilder {
         /**
          * Defines Apache Camel routes using REST DSL fluent API.
          */
         public void configure() {
             rest()
                 .get("/locations")
                     .to("direct:rest1")
                 .post("/locations")
                     .to("direct:rest2")
                 .get("/locations/{id}")
                     .param()
                         .name("id")
                         .type(RestParamType.path)
                         .dataType("integer")
                         .required(true)
                     .endParam()
                     .to("direct:rest3")
                 .get("/location/phone/{id}")
                     .param()
                         .name("id")
                         .type(RestParamType.path)
                         .dataType("integer")
                         .required(true)
                     .endParam()
                     .to("direct:rest4");
         }
     }
  2. Open the generated CamelRoutes.java file. We need to first instantiate our newly created Result Processors' and include the necessary imports. Insert the following import statements into the CamelRoutes.java file:

     ...
    import com.redhat.processor.*;
    import com.redhat.model.*;
    import org.apache.camel.model.rest.RestBindingMode;
     ...
  3. As we’re using SpringBoot, we should also include the @Component declaration to the class definition statement (under the @Generated).

     ...
     /**
     * Generated from OpenApi specification by Camel REST DSL generator.
     */
    @Generated("org.apache.camel.generator.openapi.PathGenerator")
    @Component
    public final class CamelRoutes extends RouteBuilder {
     ...
  4. Next we need to include an @Override statement for our configure() method, and include references to our result processors

     ...
     @Override
     public void configure() throws Exception {
    
         ContactInfoResultProcessor ciResultProcessor = new ContactInfoResultProcessor();
         LocationResultProcessor locationResultProcessor = new LocationResultProcessor();
     ...
  5. In order to startup an HTTP server for our REST service, we need to instantiate the restConfiguration bean with the corresponding properties. Please include the following block underneath the result processor lines you inserted in the previous step:

     ...
         restConfiguration()
             .component("servlet")
            .port(8080)
            .bindingMode(RestBindingMode.json)
             .contextPath("/")
            .dataFormatProperty("prettyPrint", "true")
            .enableCORS(true)
            .apiContextPath("/api-doc")
            .apiProperty("api.title", "Location and Contact Info API")
            .apiProperty("api.version", "1.0.0")
         ;
     ...

    Notice that we now have both ResultProcessor’s instantiated, and we’ve stood-up an Servlet HTTP listener for our RESTful endpoint, together with some basic self-documenting API docs that describe our new service.

  6. Next we need to implement our Camel routes. We need to create 4 routes, each matching their associated HTTP GET / POST endpoint. Add the following code below the generated RESTdsl code in the configure() method:

     ...
         from("direct:getalllocations")
             .to("sql:select * from locations?dataSource=dataSource")
             .process(locationResultProcessor)
             .log("${body}")
     ;
    
     from("direct:getlocation")
             .to("sql:select * from locations where id=cast(:#id as int)?dataSource=dataSource")
             .process(locationResultProcessor)
             .choice()
                 .when(simple("${body.size} > 0"))
                     .setBody(simple("${body[0]}"))
                 .otherwise()
                     .setHeader("HTTP_RESPONSE_CODE",constant("404"))
             .log("${body}")
     ;
    
         from("direct:addlocation")
                    .log("Creating new location")
             .to("sql:INSERT INTO locations (id,name,lat,lng,location_type,status) VALUES (:#${body.id},:#${body.name},:#${body.location.lat},:#${body.location.lng},:#${body.type},:#${body.status});?dataSource=dataSource")
         ;
    
         from("direct:getlocationdetail")
             .to("sql:select * from location_detail where id=cast(:#id as int)?dataSource=dataSource")
             .process(ciResultProcessor)
     ;
     ...
  7. Lastly, we need to update the RESTdsl code to accommodate our new routes. Replace the existing RESTdsl block in the configure() method with the following:

     ...
        rest()
             .get("/locations")
                 .to("direct:getalllocations")
             .post("/locations")
                 .type(Location.class)
                 .to("direct:addlocation")
             .get("/locations/{id}")
                 .param()
                     .name("id")
                     .type(RestParamType.path)
                     .dataType("integer")
                     .required(true)
                 .endParam()
                 .to("direct:getlocation")
             .get("/location/phone/{id}")
                 .param()
                     .name("id")
                     .type(RestParamType.path)
                     .dataType("integer")
                     .required(true)
                 .endParam()
                 .outType(ContactInfo.class)
                 .to("direct:getlocationdetail")
         ;

    Sample CamelRoutes.java for reference, just in case if you miss any step earlier.

  8. Before we test our newly created Camel Routes, we need to update src/main/resources/application.properties to point to our Postgres database. Set the postgresql.service.name property to postgresql.{user-username}.svc so that it points to our OpenShift service.

    00-update-properties.png
  9. Now we are ready to test our new Camel route locally. To do this, type the following command at the Terminal:

    mvn spring-boot:run
  10. If the build is successful, you will see the camel routes being started locally.

Were you able to start the app? (Tip: this mvn command will not exit; look for output like: Started Application in 5.207 seconds (JVM running for 128.535))

Try to redo this section, if any problem persists have your instructor check the Kubernetes pod that contains the CodeReady Workspaces application.

Test local spring:boot service

  1. Once Spring Boot has started-up, notice the pop-up link on exposing the port 8080 as a route. Click on open in New Tab to open the route.

    che-location-service-8080-url
  2. After the tab opens, you will initially see a 404 error. Append /locations to the URI. Also make sure the URI starts with http and not https. As a result, you should receive a list of all locations.

    che-location-service-test

Were you able to retrieve a list of locations?

Try to redo this section, if any problem persists have your instructor check the Kubernetes pod that contains the CodeReady Workspaces application.

Deploy location service to OpenShift

  1. Now that we’ve tested our API service implementation locally, we can deploy it to our running OpenShift environment. First of all, stop the Spring Boot process by entering Ctrl + C in the terminal.

  2. Enter the following Maven command to deploy the application to OpenShift.

    mvn fabric8:deploy
    che-location-service-ocp-deploy
  3. If the deployment script completes successfully, navigate back to the OpenShift web console and verify the pod is running.

    op-location-service-deploy
  4. Click on the route link highlighted and append locations to the URI. Also make sure the URI starts with http and not https. Initially, you may receive a 404 error when opening the route URL, but once you append locations and refresh you should receive a list of all locations

  5. You can also search for individual locations by adjusting the URI to /locations/{id} e.g. /locations/100.

  6. Lastly, via the CodeReady Workspaces terminal, test the HTTP POST using curl. You can use the following command:

     curl --header "Content-Type: application/json" --request POST --data '{"id": 101,"name": "Kakadu","type": "HQ","status": "1","location": {"lat": "78.88436","lng": "99.05295"}}' http://location-service-{user-username}.{openshift-app-host}/locations
  7. If the HTTP POST operation is successful, you should be able to view hte location 'Kakadu' under the list of your locations after a refresh.

    add-kakadu

Were all the HTTP POST operations successful?

Try to redo this section, if any problem persists have your instructor check the Kubernetes pod that contains the web service application.

Congratulations! You have now an application to test your Swagger to RESTdsl integration.

Summary

You have now successfully created a contract-first API using a Swagger contract together with generated Camel RESTdsl, incorporating both HTTP GET and POST requests that perform select and inserts on a Postgres database table.

You can now proceed to Lab 4.