Pablo Solar VilariƱo
2020-03-25 993a7bd3b52ea98d296b809cdb2e0eb802665e64
Connect the external service with the lab application (#5)

* Added k8s files for the gossip service

* Added new section to the frontend to gather a news feed

* Added a porxy endpoint to obtain a news feed

* Making the connection to an external service OpenShift ready

* Renaming service in OCP

* Removed unused annotations

* Removed unused annotations

* Renamed variables

* Typo

* Removed configmap and using only ENV
5 files added
7 files modified
318 ■■■■■ changed files
mczernek-exchange-application/exchange/src/main/java/com/redhat/restclient/ExchangeResource.java 11 ●●●●● patch | view | raw | blame | history
mczernek-exchange-application/exchange/src/main/java/com/redhat/restclient/News.java 31 ●●●●● patch | view | raw | blame | history
mczernek-exchange-application/exchange/src/main/java/com/redhat/restclient/NewsResource.java 25 ●●●●● patch | view | raw | blame | history
mczernek-exchange-application/exchange/src/main/java/com/redhat/restclient/NewsService.java 19 ●●●●● patch | view | raw | blame | history
mczernek-exchange-application/exchange/src/main/resources/application.properties 2 ●●●●● patch | view | raw | blame | history
mczernek-exchange-application/frontend/package.json 2 ●●●●● patch | view | raw | blame | history
mczernek-exchange-application/frontend/src/App.js 4 ●●●● patch | view | raw | blame | history
mczernek-exchange-application/frontend/src/NavList.js 3 ●●●●● patch | view | raw | blame | history
mczernek-exchange-application/frontend/src/NewsBoard.js 65 ●●●●● patch | view | raw | blame | history
mczernek-exchange-application/frontend/yarn.lock 107 ●●●●● patch | view | raw | blame | history
mczernek-exchange-application/kubefiles/exchange.yml 3 ●●●●● patch | view | raw | blame | history
python-flask-gossip/kubefiles/gossip-application.yml 46 ●●●●● patch | view | raw | blame | history
mczernek-exchange-application/exchange/src/main/java/com/redhat/restclient/ExchangeResource.java
@@ -20,8 +20,19 @@
    @Inject
    @RestClient
    ExchangeService historyService;
    @Inject
    @RestClient
    NewsService newsService;
    ObjectMapper mapper = new ObjectMapper();
    @GET
    @Path("/news")
    public List<News> getFinancialNews() {
        return newsService.getFinancialNews();
    }
    @POST
    @Path("/historicalData")
    // TODO: validate whether currency Service serves the source/target currency
mczernek-exchange-application/exchange/src/main/java/com/redhat/restclient/News.java
New file
@@ -0,0 +1,31 @@
package com.redhat.restclient;
public class News {
    private String id;
    private Integer timestamp;
    private String title;
    public String getId() {
        return id;
    }
    public void setId(String id) {
        this.id = id;
    }
    public Integer getTimestamp() {
        return timestamp;
    }
    public void setTimestamp(Integer timestamp) {
        this.timestamp = timestamp;
    }
    public String getTitle() {
        return title;
    }
    public void setTitle(String title) {
        this.title = title;
    }
}
mczernek-exchange-application/exchange/src/main/java/com/redhat/restclient/NewsResource.java
New file
@@ -0,0 +1,25 @@
package com.redhat.restclient;
import org.eclipse.microprofile.rest.client.inject.RestClient;
import javax.inject.Inject;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import java.util.List;
@Path("/news")
@Produces(MediaType.APPLICATION_JSON)
public class NewsResource {
    @Inject
    @RestClient
    NewsService news;
    @GET
    public List<News> getFinancialNews() {
        return news.getFinancialNews();
    }
}
mczernek-exchange-application/exchange/src/main/java/com/redhat/restclient/NewsService.java
New file
@@ -0,0 +1,19 @@
package com.redhat.restclient;
import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import java.util.List;
@Path("/")
@RegisterRestClient
public interface NewsService {
    @GET
    @Produces(MediaType.APPLICATION_JSON)
    List<News> getFinancialNews();
}
mczernek-exchange-application/exchange/src/main/resources/application.properties
@@ -3,5 +3,7 @@
com.redhat.restclient.ExchangeService/mp-rest/scope=javax.inject.Singleton
com.redhat.restclient.CurrencyService/mp-rest/url=http://currency:5000
com.redhat.restclient.CurrencyService/mp-rest/scope=javax.inject.Singleton
com.redhat.restclient.NewsService/mp-rest/url=${NEWS_ENDPOINT}/news/finance
com.redhat.restclient.NewsService/mp-rest/scope=javax.inject.Singleton
quarkus.http.port=8080
mczernek-exchange-application/frontend/package.json
@@ -6,6 +6,8 @@
  "dependencies": {
    "@patternfly/react-charts": "^5.3.5",
    "@patternfly/react-core": "^3.140.11",
    "@patternfly/react-table": "^2.28.10",
    "@patternfly/react-icons": "^3.15.12",
    "@testing-library/jest-dom": "^4.2.4",
    "@testing-library/react": "^9.3.2",
    "@testing-library/user-event": "^7.1.2",
mczernek-exchange-application/frontend/src/App.js
@@ -3,6 +3,7 @@
import HistoricalDataForm from './HistoricalDataForm'
import WelcomePage from './Welcome'
import SingleCurrencyExchange from './SingleCurrencyExchange'
import NewsBoard from './NewsBoard'
import {
  BrowserRouter as Router,
@@ -26,6 +27,9 @@
            <Route path="/history" exact>
              <HistoricalDataForm />
            </Route>
            <Route path="/news" exact>
              <NewsBoard />
            </Route>
          </Structure>
        </Switch>
      </Router>
mczernek-exchange-application/frontend/src/NavList.js
@@ -23,6 +23,9 @@
                    <NavItem id="exchange" isActive={window.location.pathname.endsWith("/exchange")}>
                        <Link to="/exchange" >Exchange</Link>
                    </NavItem>
                    <NavItem id="news" isActive={window.location.pathname.endsWith("/news")}>
                        <Link to="/news" >News</Link>
                    </NavItem>
                </NavList>
            </Nav>
        );
mczernek-exchange-application/frontend/src/NewsBoard.js
New file
@@ -0,0 +1,65 @@
import React, { Component } from 'react';
import { Table, TableHeader, TableBody } from '@patternfly/react-table';
import { EmptyState, EmptyStateIcon, EmptyStateBody, EmptyStateVariant, Bullseye, Title } from '@patternfly/react-core';
import { ErrorCircleOIcon } from '@patternfly/react-icons'
class NewsBoard extends Component {
    normalize(data) {
        return data.map(function (element) {
            return {
                cells: [element.timestamp, element.title]
            }
        });
    }
    componentDidMount() {
        fetch(`http://${process.env.REACT_APP_GW_ENDPOINT}/news`)
            .then(res => res.json())
            .then((data) => {
                this.setState({ rows: this.normalize(data) });
            })
            .catch(console.log)
    }
    constructor(props) {
        super(props);
        this.state = {
            columns: ['Timestamp', 'Story'],
            rows: [{
                heightAuto: true,
                cells: [
                    {
                        props: { colSpan: 2 },
                        title: (
                            <Bullseye>
                                <EmptyState variant={EmptyStateVariant.small}>
                                    <EmptyStateIcon icon={ErrorCircleOIcon} />
                                    <Title headingLevel="h2" size="lg">
                                        No results found
                                    </Title>
                                    <EmptyStateBody>
                                        Unable to get news from external feed.
                                    </EmptyStateBody>
                                </EmptyState>
                            </Bullseye>
                        )
                    },
                ]
            }]
        }
    }
    render() {
        const { columns, rows } = this.state;
        return (
            <Table caption="Latest News" rows={rows} cells={columns}>
            <TableHeader />
            <TableBody />
            </Table>
        )
    }
}
export default NewsBoard;
mczernek-exchange-application/frontend/yarn.lock
@@ -1156,6 +1156,11 @@
  resolved "https://registry.yarnpkg.com/@patternfly/patternfly/-/patternfly-2.65.2.tgz#d59359d8ed45f55ea466b0976328aeb2324cd2de"
  integrity sha512-Q0Hi1JF7wuhs9fpYhItT2ZNv+JVqo1VvFEKvtjBQsPJdhCl9t7YAyOhTce4QK0ORj47lnVP3mw+1sjIWY0SPlA==
"@patternfly/patternfly@2.68.3":
  version "2.68.3"
  resolved "https://registry.yarnpkg.com/@patternfly/patternfly/-/patternfly-2.68.3.tgz#fd2b8b92618cd9adde714d46f33ae050d7a3d4f0"
  integrity sha512-ZsANYyp/AVRaSWAFBnKuoyvVoA1tMT7hh1jHpua0sQYjN4ocuN89MNcNdAJhdawt1Bh+p9OEtDgl4vfKQbJ9RQ==
"@patternfly/react-charts@^5.3.5":
  version "5.3.5"
  resolved "https://registry.yarnpkg.com/@patternfly/react-charts/-/react-charts-5.3.5.tgz#069bd8da400adddae104bf3f487c132fe891342e"
@@ -1183,6 +1188,27 @@
    focus-trap-react "^4.0.1"
    tippy.js "5.1.2"
"@patternfly/react-core@^3.146.0":
  version "3.146.0"
  resolved "https://registry.yarnpkg.com/@patternfly/react-core/-/react-core-3.146.0.tgz#81f06d6f0e0c122e93e303aa469befed47a767ee"
  integrity sha512-uzzTmOMnvbmpFOjUFqQn2PHPVKvpgbqePrNutPHCHbYcTXXF16t4YABk9S5POpeEqs9ETIRrkLnNa8e9mKYGog==
  dependencies:
    "@patternfly/react-icons" "^3.15.11"
    "@patternfly/react-styles" "^3.7.8"
    "@patternfly/react-tokens" "^2.8.8"
    emotion "^9.2.9"
    exenv "^1.2.2"
    focus-trap-react "^4.0.1"
    react-dropzone "9.0.0"
    tippy.js "5.1.2"
"@patternfly/react-icons@^3.15.11", "@patternfly/react-icons@^3.15.12":
  version "3.15.12"
  resolved "https://registry.yarnpkg.com/@patternfly/react-icons/-/react-icons-3.15.12.tgz#7c80672d5bff4a7ea2d24abdd9e8477d79667e45"
  integrity sha512-UX2mkfYp7NunBTBrsd4RamPh4pTTqhIxjvx23n19xLs9doXEMjZr1+xMuswrg+68EpCDA5gngeFWKRnb8vYXKg==
  dependencies:
    "@fortawesome/free-brands-svg-icons" "^5.8.1"
"@patternfly/react-icons@^3.15.3":
  version "3.15.4"
  resolved "https://registry.yarnpkg.com/@patternfly/react-icons/-/react-icons-3.15.4.tgz#f5b2c5a53b4ce6c912d9fb27585b251048d4b167"
@@ -1201,10 +1227,40 @@
    emotion "^9.2.9"
    emotion-server "^9.2.9"
"@patternfly/react-styles@^3.7.8":
  version "3.7.8"
  resolved "https://registry.yarnpkg.com/@patternfly/react-styles/-/react-styles-3.7.8.tgz#6cfac5a7e7c8c49520f0f9276734036ceece9b85"
  integrity sha512-9LxcVVQdYEN7SJz3nwvrK4W9rci2c74DDyCbMJb2rC3VAOKKHb7We9iDn+2vxHV43Oskc2vfRoKjPEdVJcXYAA==
  dependencies:
    camel-case "^3.0.0"
    css "^2.2.3"
    cssstyle "^0.3.1"
    emotion "^9.2.9"
    emotion-server "^9.2.9"
"@patternfly/react-table@^2.28.10":
  version "2.28.10"
  resolved "https://registry.yarnpkg.com/@patternfly/react-table/-/react-table-2.28.10.tgz#829a99cf3a44a60586ab06a3930c44b411f1f8d6"
  integrity sha512-OPonTCMVsumEOUNc6u6WuxVWsqsTODnhmFMJcL9IO3QWN19CGYqEY9EQX0yfPuGRVhkAD4zutm5XsEpxg/mYbQ==
  dependencies:
    "@patternfly/patternfly" "2.68.3"
    "@patternfly/react-core" "^3.146.0"
    "@patternfly/react-icons" "^3.15.11"
    "@patternfly/react-styles" "^3.7.8"
    "@patternfly/react-tokens" "^2.8.8"
    classnames "^2.2.5"
    exenv "^1.2.2"
    lodash "^4.17.15"
"@patternfly/react-tokens@^2.8.4":
  version "2.8.4"
  resolved "https://registry.yarnpkg.com/@patternfly/react-tokens/-/react-tokens-2.8.4.tgz#20cb17f2196a25d9cc6ae4cc0e79686ea9bf0bbd"
  integrity sha512-GlLyutls0bG39Nwl/sv2FUkicwyRNrXQFso+e7Y4470+VOUtSsVSdQz+rTjgPxQ38olKPsSZdtEjqN9o2PbDiw==
"@patternfly/react-tokens@^2.8.8":
  version "2.8.8"
  resolved "https://registry.yarnpkg.com/@patternfly/react-tokens/-/react-tokens-2.8.8.tgz#25edde60501708b8fe821f69ab1f284ac1b81814"
  integrity sha512-W3Kx2zlbfFtcjL9mYpjWNU9RfNrFpfXI6zC8qQTuaDwhV3IHCWt5HCkcf9oIW2aCtEjC0EkI67Wbz0BcmdYGJA==
"@sheerun/mutationobserver-shim@^0.3.2":
  version "0.3.2"
@@ -2050,6 +2106,13 @@
  resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9"
  integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==
attr-accept@^1.1.3:
  version "1.1.3"
  resolved "https://registry.yarnpkg.com/attr-accept/-/attr-accept-1.1.3.tgz#48230c79f93790ef2775fcec4f0db0f5db41ca52"
  integrity sha512-iT40nudw8zmCweivz6j58g+RT33I4KbaIvRUhjNmDwO2WmsQUxFEZZYZ5w3vXe5x5MX9D7mfvA/XaLOZYFR9EQ==
  dependencies:
    core-js "^2.5.0"
autoprefixer@^9.6.1:
  version "9.7.4"
  resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-9.7.4.tgz#f8bf3e06707d047f0641d87aee8cfb174b2a5378"
@@ -2757,6 +2820,11 @@
    isobject "^3.0.0"
    static-extend "^0.1.1"
classnames@^2.2.5:
  version "2.2.6"
  resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.2.6.tgz#43935bffdd291f326dad0a205309b38d00f650ce"
  integrity sha512-JR/iSQOSt+LQIWwrwEzJ9uk0xfN3mTVYMwt1Ir5mUcSN6pU+V4zQFFaJsclJbPuAUQH+yfWef6tm7l1quW3C8Q==
clean-css@^4.2.1:
  version "4.2.3"
  resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-4.2.3.tgz#507b5de7d97b48ee53d84adb0160ff6216380f78"
@@ -3037,7 +3105,7 @@
    browserslist "^4.8.3"
    semver "7.0.0"
core-js@^2.4.0:
core-js@^2.4.0, core-js@^2.5.0:
  version "2.6.11"
  resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.11.tgz#38831469f9922bded8ee21c9dc46985e0399308c"
  integrity sha512-5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg==
@@ -4461,6 +4529,13 @@
  dependencies:
    loader-utils "^1.2.3"
    schema-utils "^2.5.0"
file-selector@^0.1.8:
  version "0.1.12"
  resolved "https://registry.yarnpkg.com/file-selector/-/file-selector-0.1.12.tgz#fe726547be219a787a9dcc640575a04a032b1fd0"
  integrity sha512-Kx7RTzxyQipHuiqyZGf+Nz4vY9R1XGxuQl/hLoJwq+J4avk/9wxxgZyHKtbyIPJmbD4A66DWGYfyykWNpcYutQ==
  dependencies:
    tslib "^1.9.0"
file-uri-to-path@1.0.0:
  version "1.0.0"
@@ -8438,6 +8513,14 @@
    kleur "^3.0.3"
    sisteransi "^1.0.3"
prop-types-extra@^1.1.0:
  version "1.1.1"
  resolved "https://registry.yarnpkg.com/prop-types-extra/-/prop-types-extra-1.1.1.tgz#58c3b74cbfbb95d304625975aa2f0848329a010b"
  integrity sha512-59+AHNnHYCdiC+vMwY52WmvP5dM3QLeoumYuEyceQDi9aEhtwN9zIQ2ZNo25sMyXnbh32h+P1ezDsUpUH3JAew==
  dependencies:
    react-is "^16.3.2"
    warning "^4.0.0"
prop-types@^15.5.8, prop-types@^15.6.2, prop-types@^15.7.2:
  version "15.7.2"
  resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5"
@@ -8644,6 +8727,16 @@
    prop-types "^15.6.2"
    scheduler "^0.19.0"
react-dropzone@9.0.0:
  version "9.0.0"
  resolved "https://registry.yarnpkg.com/react-dropzone/-/react-dropzone-9.0.0.tgz#4f5223cdcb4d3bd8a66e3298c4041eb0c75c4634"
  integrity sha512-wZ2o9B2qkdE3RumWhfyZT9swgJYJPeU5qHEcMU8weYpmLex1eeWX0CC32/Y0VutB+BBi2D+iePV/YZIiB4kZGw==
  dependencies:
    attr-accept "^1.1.3"
    file-selector "^0.1.8"
    prop-types "^15.6.2"
    prop-types-extra "^1.1.0"
react-error-overlay@^6.0.6:
  version "6.0.6"
  resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-6.0.6.tgz#ac4d9dc4c1b5c536c2c312bf66aa2b09bfa384e2"
@@ -8653,6 +8746,11 @@
  version "2.0.4"
  resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-2.0.4.tgz#e84b4d455b0fec113e0402c329352715196f81f9"
  integrity sha512-suNP+J1VU1MWFKcyt7RtjiSWUjvidmQSlqu+eHslq+342xCbGTYmC0mEhPCOHxlW0CywylOC1u2DFAT+bv4dBw==
react-is@^16.3.2:
  version "16.13.1"
  resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
  integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
react-is@^16.6.0, react-is@^16.7.0:
  version "16.13.0"
@@ -10798,6 +10896,13 @@
  dependencies:
    makeerror "1.0.x"
warning@^4.0.0:
  version "4.0.3"
  resolved "https://registry.yarnpkg.com/warning/-/warning-4.0.3.tgz#16e9e077eb8a86d6af7d64aa1e05fd85b4678ca3"
  integrity sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==
  dependencies:
    loose-envify "^1.0.0"
watchpack@^1.6.0:
  version "1.6.0"
  resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-1.6.0.tgz#4bc12c2ebe8aa277a71f1d3f14d685c7b446cd00"
mczernek-exchange-application/kubefiles/exchange.yml
@@ -19,6 +19,9 @@
          image: quay.io/redhattraining/ossm-exchange:1.0
          ports:
            - containerPort: 8080
          env:
            - name: NEWS_ENDPOINT
              value: "http://feed-news.apps-crc.testing"
---
apiVersion: v1
kind: Service
python-flask-gossip/kubefiles/gossip-application.yml
New file
@@ -0,0 +1,46 @@
apiVersion: apps/v1
kind: Deployment
metadata:
  name: news
spec:
  selector:
    matchLabels:
      app: news
  replicas: 1
  template:
    metadata:
      labels:
        app: news
    spec:
      containers:
        - name: news
          image: quay.io/redhattraining/ossm-python-flask-gossip:1.0
          ports:
            - containerPort: 5000
---
apiVersion: v1
kind: Service
metadata:
  labels:
    app: news
  name: news
spec:
  ports:
  - port: 5000
    protocol: TCP
    targetPort: 5000
  selector:
    app: news
---
kind: Route
apiVersion: route.openshift.io/v1
metadata:
  name: news
  labels:
    app: news
spec:
  to:
    kind: Service
    name: news
  port:
    targetPort: 5000