diff --git a/spring-petclinic-microservices/.editorconfig b/spring-petclinic-microservices/.editorconfig new file mode 100644 index 000000000..8d67bc7a5 --- /dev/null +++ b/spring-petclinic-microservices/.editorconfig @@ -0,0 +1,12 @@ +# top-most EditorConfig file +root = true + +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +indent_style = space + +[*.{java,xml}] +indent_size = 4 +trim_trailing_whitespace = true diff --git a/spring-petclinic-microservices/.gitignore b/spring-petclinic-microservices/.gitignore new file mode 100644 index 000000000..f2ba99b0c --- /dev/null +++ b/spring-petclinic-microservices/.gitignore @@ -0,0 +1,21 @@ +# Maven +target/ +!.mvn/wrapper/maven-wrapper.jar + +# Jenv +.java-version + +# Eclipse +.settings/ +.classpath +.project + +# IntelliJ IDEA +.idea +*.iml + +# Branch switching +generated/ + +# Skip scripts +.scripts/ diff --git a/spring-petclinic-microservices/.mvn/wrapper/MavenWrapperDownloader.java b/spring-petclinic-microservices/.mvn/wrapper/MavenWrapperDownloader.java new file mode 100644 index 000000000..1ef8d6981 --- /dev/null +++ b/spring-petclinic-microservices/.mvn/wrapper/MavenWrapperDownloader.java @@ -0,0 +1,117 @@ +/* + * Copyright 2007-present the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import java.net.*; +import java.io.*; +import java.nio.channels.*; +import java.util.Properties; + +public class MavenWrapperDownloader { + + private static final String WRAPPER_VERSION = "0.5.4"; + /** + * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided. + */ + private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/" + + WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + " .jar"; + + /** + * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to + * use instead of the default one. + */ + private static final String MAVEN_WRAPPER_PROPERTIES_PATH = + ".mvn/wrapper/maven-wrapper.properties"; + + /** + * Path where the maven-wrapper.jar will be saved to. + */ + private static final String MAVEN_WRAPPER_JAR_PATH = + ".mvn/wrapper/maven-wrapper.jar"; + + /** + * Name of the property which should be used to override the default download url for the wrapper. + */ + private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl"; + + public static void main(String args[]) { + System.out.println("- Downloader started"); + File baseDirectory = new File(args[0]); + System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath()); + + // If the maven-wrapper.properties exists, read it and check if it contains a custom + // wrapperUrl parameter. + File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH); + String url = DEFAULT_DOWNLOAD_URL; + if(mavenWrapperPropertyFile.exists()) { + FileInputStream mavenWrapperPropertyFileInputStream = null; + try { + mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile); + Properties mavenWrapperProperties = new Properties(); + mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream); + url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url); + } catch (IOException e) { + System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'"); + } finally { + try { + if(mavenWrapperPropertyFileInputStream != null) { + mavenWrapperPropertyFileInputStream.close(); + } + } catch (IOException e) { + // Ignore ... + } + } + } + System.out.println("- Downloading from: " + url); + + File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH); + if(!outputFile.getParentFile().exists()) { + if(!outputFile.getParentFile().mkdirs()) { + System.out.println( + "- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'"); + } + } + System.out.println("- Downloading to: " + outputFile.getAbsolutePath()); + try { + downloadFileFromURL(url, outputFile); + System.out.println("Done"); + System.exit(0); + } catch (Throwable e) { + System.out.println("- Error downloading"); + e.printStackTrace(); + System.exit(1); + } + } + + private static void downloadFileFromURL(String urlString, File destination) throws Exception { + if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) { + String username = System.getenv("MVNW_USERNAME"); + char[] password = System.getenv("MVNW_PASSWORD").toCharArray(); + Authenticator.setDefault(new Authenticator() { + @Override + protected PasswordAuthentication getPasswordAuthentication() { + return new PasswordAuthentication(username, password); + } + }); + } + URL website = new URL(urlString); + ReadableByteChannel rbc; + rbc = Channels.newChannel(website.openStream()); + FileOutputStream fos = new FileOutputStream(destination); + fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE); + fos.close(); + rbc.close(); + } + +} diff --git a/spring-petclinic-microservices/.mvn/wrapper/maven-wrapper.jar b/spring-petclinic-microservices/.mvn/wrapper/maven-wrapper.jar new file mode 100644 index 000000000..1914b842c Binary files /dev/null and b/spring-petclinic-microservices/.mvn/wrapper/maven-wrapper.jar differ diff --git a/spring-petclinic-microservices/.mvn/wrapper/maven-wrapper.properties b/spring-petclinic-microservices/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 000000000..23fdba01f --- /dev/null +++ b/spring-petclinic-microservices/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1,2 @@ +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.5.4/apache-maven-3.5.4-bin.zip +wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.4/maven-wrapper-0.5.4.jar diff --git a/spring-petclinic-microservices/.scripts/setup-env-variables-azure-template.sh b/spring-petclinic-microservices/.scripts/setup-env-variables-azure-template.sh new file mode 100755 index 000000000..9ee5fdb54 --- /dev/null +++ b/spring-petclinic-microservices/.scripts/setup-env-variables-azure-template.sh @@ -0,0 +1,65 @@ +#!/usr/bin/env bash + +# ==== Resource Group ==== +export SUBSCRIPTION=subscription-id # customize this +export RESOURCE_GROUP=resource-group-name # customize this +export LOCATION=SouthCentralUS #customize this +export COSMOSDB_NAME=mycosmosdbaccname # customize this +export REDIS_NAME=myredisname #customize this +export KEYVAULT_NAME=myend2endkv #customize this +export APP_NAME_FOR_KEYVAULT=myappforkeyvault #customize this + +# ==== Create ResouceGroup ==== +echo "Creating ResouceGroup, it may take a few minutes" +az group create --name ${RESOURCE_GROUP} \ + --location ${LOCATION} + +# ==== Create CosmosDB Account ==== +echo "Creating CosmosDB Account, it may take a few minutes" +az cosmosdb create --name $COSMOSDB_NAME --resource-group $RESOURCE_GROUP +COSMOS_KEYS=$(az cosmosdb keys list --name $COSMOSDB_NAME --resource-group $RESOURCE_GROUP --type keys) +COSMOS_PRIMARY_KEY=$(echo $COSMOS_KEYS | jq -r .primaryMasterKey) +COSMOS_SECONDARY_KEY=$(echo $COSMOS_KEYS | jq -r .secondaryMasterKey) +COSMOSDB_URI=$(az cosmosdb show --name $COSMOSDB_NAME --resource-group $RESOURCE_GROUP | jq -r .documentEndpoint) + +# ==== Create Redis Cache Account ==== +echo "Creating Redis Cache Account, it may take a few minutes" +az redis create --name $REDIS_NAME --resource-group $RESOURCE_GROUP --sku Basic --vm-size c0 --location $LOCATION +REDIS_HOSTNAME=$(az redis show --name $REDIS_NAME --resource-group $RESOURCE_GROUP | jq -r .hostName) +REDIS_PASSWORD=$(az redis list-keys --name $REDIS_NAME --resource-group $RESOURCE_GROUP | jq -r .primaryKey) + +# ==== Create KeyVault Account ==== +echo "Creating KeyVault Account, it may take a few minutes" +az keyvault create --location $LOCATION --name $KEYVAULT_NAME --resource-group $RESOURCE_GROUP + +SERVICE_PRINCIPAL=$(az ad sp create-for-rbac --name ${APP_NAME_FOR_KEYVAULT} --role Contributor) + +AZURE_KEYVAULT_URI=$(az keyvault show --name $KEYVAULT_NAME --resource-group $RESOURCE_GROUP | jq -r .properties | jq -r .vaultUri) +AZURE_KEYVAULT_CLIENTID=$(echo $SERVICE_PRINCIPAL | jq -r .appId) +AZURE_KEYVAULT_TENANTID=$(echo $SERVICE_PRINCIPAL | jq -r .tenant) +AZURE_KEYVAULT_CLIENTKEY=$(echo $SERVICE_PRINCIPAL | jq -r .password) + +# set keyvault policy +az keyvault set-policy -n $KEYVAULT_NAME --key-permissions get list \ + --certificate-permissions get list \ + --secret-permissions get list \ + --resource-group $RESOURCE_GROUP \ + --spn $AZURE_KEYVAULT_CLIENTID + +# ==== add keys to keyvault ==== +echo "add keys to keyvault" +az keyvault secret set --vault-name $KEYVAULT_NAME --name cosmosdburi --value $COSMOSDB_URI +az keyvault secret set --vault-name $KEYVAULT_NAME --name cosmosdbkey --value $COSMOS_PRIMARY_KEY +az keyvault secret set --vault-name $KEYVAULT_NAME --name cosmosdbsecondarykey --value $COSMOS_SECONDARY_KEY +az keyvault secret set --vault-name $KEYVAULT_NAME --name redisuri --value $REDIS_HOSTNAME +az keyvault secret set --vault-name $KEYVAULT_NAME --name redispassword --value $REDIS_PASSWORD + + +# ==== Create Kevvault environment file for Docker containers ==== +cat > keyvault.env << EOF +AZURE_KEYVAULT_URI=$AZURE_KEYVAULT_URI +AZURE_KEYVAULT_CLIENTID=$AZURE_KEYVAULT_CLIENTID +AZURE_KEYVAULT_TENANTID=$AZURE_KEYVAULT_TENANTID +AZURE_KEYVAULT_CLIENTKEY=$AZURE_KEYVAULT_CLIENTKEY +EOF +echo "environment configuration complete" diff --git a/spring-petclinic-microservices/.travis.yml b/spring-petclinic-microservices/.travis.yml new file mode 100644 index 000000000..9b16d5771 --- /dev/null +++ b/spring-petclinic-microservices/.travis.yml @@ -0,0 +1,2 @@ +language: java +jdk: openjdk8 diff --git a/spring-petclinic-microservices/LICENSE b/spring-petclinic-microservices/LICENSE new file mode 100644 index 000000000..91ef974e0 --- /dev/null +++ b/spring-petclinic-microservices/LICENSE @@ -0,0 +1,13 @@ +Copyright 2002-2020 the original author or authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/spring-petclinic-microservices/README-petclinic.md b/spring-petclinic-microservices/README-petclinic.md new file mode 100644 index 000000000..db5016f2b --- /dev/null +++ b/spring-petclinic-microservices/README-petclinic.md @@ -0,0 +1,128 @@ +# Distributed version of the Spring PetClinic Sample Application built with Spring Cloud + +[![Build Status](https://travis-ci.org/spring-petclinic/spring-petclinic-microservices.svg?branch=master)](https://travis-ci.org/spring-petclinic/spring-petclinic-microservices/) [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) + +This microservices branch was initially derived from [AngularJS version](https://github.com/spring-petclinic/spring-petclinic-angular1) to demonstrate how to split sample Spring application into [microservices](http://www.martinfowler.com/articles/microservices.html). +To achieve that goal we use Spring Cloud Gateway, Spring Cloud Circuit Breaker, Spring Cloud Config, Spring Cloud Sleuth, Resilience4j, Micrometer +and the Eureka Service Discovery from the [Spring Cloud Netflix](https://github.com/spring-cloud/spring-cloud-netflix) technology stack. + +## Starting services locally without Docker + +Every microservice is a Spring Boot application and can be started locally using IDE or `../mvnw spring-boot:run` command. Please note that supporting services (Config and Discovery Server) must be started before any other application (Customers, Vets, Visits and API). +Startup of Tracing server, Admin server, Grafana and Prometheus is optional. +If everything goes well, you can access the following services at given location: +* Discovery Server - http://localhost:8761 +* Config Server - http://localhost:8888 +* AngularJS frontend (API Gateway) - http://localhost:8080 +* Customers, Vets and Visits Services - random port, check Eureka Dashboard +* Tracing Server (Zipkin) - http://localhost:9411/zipkin/ (we use [openzipkin](https://github.com/openzipkin/zipkin/tree/master/zipkin-server)) +* Admin Server (Spring Boot Admin) - http://localhost:9090 +* Grafana Dashboards - http://localhost:3000 +* Prometheus - http://localhost:9091 + +You can tell Config Server to use your local Git repository by using `native` Spring profile and setting +`GIT_REPO` environment variable, for example: +`-Dspring.profiles.active=native -DGIT_REPO=/projects/spring-petclinic-microservices-config` + +## Starting services locally with docker-compose +In order to start entire infrastructure using Docker, you have to build images by executing `./mvnw clean install -P buildDocker` +from a project root. Once images are ready, you can start them with a single command +`docker-compose up`. Containers startup order is coordinated with [`dockerize` script](https://github.com/jwilder/dockerize). +After starting services it takes a while for API Gateway to be in sync with service registry, +so don't be scared of initial Spring Cloud Gateway timeouts. You can track services availability using Eureka dashboard +available by default at http://localhost:8761. + +The `master` branch uses an Alpine linux with JRE 8 as Docker base. You will find a Java 11 version in the `release/java11` branch. + +*NOTE: Under MacOSX or Windows, make sure that the Docker VM has enough memory to run the microservices. The default settings +are usually not enough and make the `docker-compose up` painfully slow.* + +## Understanding the Spring Petclinic application + +[See the presentation of the Spring Petclinic Framework version](http://fr.slideshare.net/AntoineRey/spring-framework-petclinic-sample-application) + +[A blog bost introducing the Spring Petclinic Microsevices](http://javaetmoi.com/2018/10/architecture-microservices-avec-spring-cloud/) (french language) + +You can then access petclinic here: http://localhost:8080/ + +![Spring Petclinic Microservices screenshot](docs/application-screenshot.png) + +**Architecture diagram of the Spring Petclinic Microservices with CosmosDB** + +![Spring Petclinic Microservices architecture](docs/microservices-architecture-diagram-cosmosdb.jpg) + +## In case you find a bug/suggested improvement for Spring Petclinic Microservices + +Our issue tracker is available here: https://github.com/spring-petclinic/spring-petclinic-microservices/issues + +## Custom metrics monitoring + +Grafana and Prometheus are included in the `docker-compose.yml` configuration, and the public facing applications +have been instrumented with [MicroMeter](https://micrometer.io) to collect JVM and custom business metrics. + +A JMeter load testing script is available to stress the application and generate metrics: [petclinic_test_plan.jmx](spring-petclinic-api-gateway/src/test/jmeter/petclinic_test_plan.jmx) + +![Grafana metrics dashboard](docs/grafana-custom-metrics-dashboard.png) + +### Using Prometheus + +* Prometheus can be accessed from your local machine at http://localhost:9091 + +### Using Grafana with Prometheus + +* An anonymous access and a Prometheus datasource are setup. +* A `Spring Petclinic Metrics` Dashboard is available at the URL http://localhost:3000/d/69JXeR0iw/spring-petclinic-metrics. +You will find the JSON configuration file here: [docker/grafana/dashboards/grafana-petclinic-dashboard.json](). +* You may create your own dashboard or import the [Micrometer/SpringBoot dashboard](https://grafana.com/dashboards/4701) via the Import Dashboard menu item. +The id for this dashboard is `4701`. + +### Custom metrics +Spring Boot registers a lot number of core metrics: JVM, CPU, Tomcat, Logback... +The Spring Boot auto-configuration enables the instrumentation of requests handled by Spring MVC. +All those three REST controllers `OwnerResource`, `PetResource` and `VisitResource` have been instrumented by the `@Timed` Micrometer annotation at class level. + +* `customers-service` application has the following custom metrics enabled: + * @Timed: `petclinic.owner` + * @Timed: `petclinic.pet` +* `visits-service` application has the following custom metrics enabled: + * @Timed: `petclinic.visit` + +## Looking for something in particular? + +| Spring Cloud components | Resources | +|---------------------------------|------------| +| Configuration server | [Config server properties](spring-petclinic-config-server/src/main/resources/application.yml) and [Configuration repository] | +| Service Discovery | [Eureka server](spring-petclinic-discovery-server) and [Service discovery client](spring-petclinic-vets-service/src/main/java/org/springframework/samples/petclinic/vets/VetsServiceApplication.java) | +| API Gateway | [Spring Cloud Gateway starter](spring-petclinic-api-gateway/pom.xml) and [Routing configuration](/spring-petclinic-api-gateway/src/main/resources/application.yml) | +| Docker Compose | [Spring Boot with Docker guide](https://spring.io/guides/gs/spring-boot-docker/) and [docker-compose file](docker-compose.yml) | +| Circuit Breaker | [Resilience4j fallback method](spring-petclinic-api-gateway/src/main/java/org/springframework/samples/petclinic/api/boundary/web/ApiGatewayController.java) | +| Grafana / Prometheus Monitoring | [Micrometer implementation](https://micrometer.io/), [Spring Boot Actuator Production Ready Metrics] | + + Front-end module | Files | +|-------------------|-------| +| Node and NPM | [The frontend-maven-plugin plugin downloads/installs Node and NPM locally then runs Bower and Gulp](spring-petclinic-ui/pom.xml) | +| Bower | [JavaScript libraries are defined by the manifest file bower.json](spring-petclinic-ui/bower.json) | +| Gulp | [Tasks automated by Gulp: minify CSS and JS, generate CSS from LESS, copy other static resources](spring-petclinic-ui/gulpfile.js) | +| Angular JS | [app.js, controllers and templates](spring-petclinic-ui/src/scripts/) | + + +## Interesting Spring Petclinic forks + +The Spring Petclinic master branch in the main [spring-projects](https://github.com/spring-projects/spring-petclinic) +GitHub org is the "canonical" implementation, currently based on Spring Boot and Thymeleaf. + +This [spring-petclinic-microservices](https://github.com/spring-petclinic/spring-petclinic-microservices/) project is one of the [several forks](https://spring-petclinic.github.io/docs/forks.html) +hosted in a special GitHub org: [spring-petclinic](https://github.com/spring-petclinic). +If you have a special interest in a different technology stack +that could be used to implement the Pet Clinic then please join the community there. + + +# Contributing + +The [issue tracker](https://github.com/spring-petclinic/spring-petclinic-microservices/issues) is the preferred channel for bug reports, features requests and submitting pull requests. + +For pull requests, editor preferences are available in the [editor config](.editorconfig) for easy use in common text editors. Read more and download plugins at . + + +[Configuration repository]: https://github.com/spring-petclinic/spring-petclinic-microservices-config +[Spring Boot Actuator Production Ready Metrics]: https://docs.spring.io/spring-boot/docs/current/reference/html/production-ready-metrics.html diff --git a/spring-petclinic-microservices/README.md b/spring-petclinic-microservices/README.md new file mode 100644 index 000000000..537a996ed --- /dev/null +++ b/spring-petclinic-microservices/README.md @@ -0,0 +1,152 @@ +--- +page_type: sample +languages: +- java +products: +- Azure Spring Integration Starters +description: "Deploy Spring microservices using Spring Integration starters for Azure" +urlFragment: "spring-petclinic-microservices" +--- +# Deploy Spring Microservices using Azure Spring starters for Cosmos DB, Redis, KeyVault + +Azure Spring Cloud enables you to easily run a Spring Boot based microservices application on Azure. + +This quickstart shows you how to deploy an existing Java Spring Cloud application to Azure. When you're finished, you can continue to manage the application via the Azure CLI or switch to using the Azure portal. + +## What will you experience +You will: +- Build existing Spring microservices applications +- Provision azure resources required for the application +- Run the application locally with CosmosDB backend, Azure Redis Cache and using KeyVault for storing secrets +- Open the application + +## What you will need + +In order to deploy a Java app to cloud, you need +an Azure subscription. If you do not already have an Azure +subscription, you can activate your +[MSDN subscriber benefits](https://azure.microsoft.com/pricing/member-offers/msdn-benefits-details/) +or sign up for a +[free Azure account]((https://azure.microsoft.com/free/)). + +In addition, you will need the following: + +| [Azure CLI version 2.0.67 or higher](https://docs.microsoft.com/cli/azure/install-azure-cli?view=azure-cli-latest) +| [Java 8](https://www.azul.com/downloads/azure-only/zulu/?version=java-8-lts&architecture=x86-64-bit&package=jdk) +| [Maven](https://maven.apache.org/download.cgi) +| [MySQL CLI](https://dev.mysql.com/downloads/shell/) +| [Git](https://git-scm.com/) +| [Jq](https://stedolan.github.io/jq/) +| + +## Clone and build the repo + +### Create a new folder and clone the sample app repository to your Azure Cloud account + +```bash + mkdir source-code + git clone https://github.com/Azure-Samples/azure-spring-boot-samples +``` + +### Change directory and build the project + +```bash + cd azure-spring-boot-samples/spring-petclinic-microservices + mvn clean package -DskipTests +``` +This will take a few minutes. + +## Provision Azure resources using Azure CLI + + +### Login to Azure +Login to the Azure CLI and choose your active subscription. Be sure to choose the active subscription that is whitelisted for Azure Spring Cloud + +```bash + az login + az account list -o table + az account set --subscription ${SUBSCRIPTION} +``` + +### Prepare your environment for deployments + +Create a bash script with environment variables by making a copy of the supplied template: +```bash + cp .scripts/setup-env-variables-azure-template.sh .scripts/setup-env-variables-azure.sh +``` + +Open `.scripts/setup-env-variables-azure.sh` and enter the following information: + +```bash + +export SUBSCRIPTION=subscription-id # customize this +export RESOURCE_GROUP=resource-group-name # customize this +export LOCATION=SouthCentralUS #customize this +export COSMOSDB_NAME=mycosmosdbaccname # customize this +export REDIS_NAME=myredisname #customize this +export KEYVAULT_NAME=myend2endkv #customize this +export APP_NAME_FOR_KEYVAULT=myappforkeyvault #customize this + +``` + +Then, set the environment: +```bash + source .scripts/setup-env-variables-azure.sh +``` + +make sure keyvault.env file is created at the root of the repo. + +## Starting services locally with docker-compose +In order to start entire infrastructure using Docker, you have to build images by executing `./mvnw clean install -P buildDocker -DskipTests` +from a project root. Once images are ready, you can start them with a single command +`docker-compose up`. Containers startup order is coordinated with [`dockerize` script](https://github.com/jwilder/dockerize). +After starting services it takes a while for API Gateway to be in sync with service registry, +so don't be scared of initial Spring Cloud Gateway timeouts. You can track services availability using Eureka dashboard +available by default at http://localhost:8761. + +## Understanding the Spring Petclinic application +[See the presentation of the Spring Petclinic Framework version](http://fr.slideshare.net/AntoineRey/spring-framework-petclinic-sample-application) + +[A blog bost introducing the Spring Petclinic Microsevices](http://javaetmoi.com/2018/10/architecture-microservices-avec-spring-cloud/) (french language) + +You can then access petclinic here: http://localhost:8080/ + +![Spring Petclinic Microservices screenshot](docs/application-screenshot.png) + +**Architecture diagram of the Spring Petclinic Microservices with CosmosDB** + +![Spring Petclinic Microservices architecture](docs/microservices-architecture-diagram-cosmosdb.jpg) + + +## Navigate to the application +The application could be reached at http://localhost:8080 +![](./media/petclinic.jpg) + +## Enabling Spring boot starter for Azure Active directory (Optional) +To secure the Java applications in this sample please follow the [Spring Security Azure Active Directory tutorial](https://docs.microsoft.com/en-us/azure/developer/java/spring-framework/configure-spring-boot-starter-java-app-with-azure-active-directory). +After setting your Active directory you can enable security on Customers service by uncommenting the relevant AAD sample code. + +## Next Steps + +In this quickstart, you've deployed an existing Spring microservices app using Azure CLI. To learn more about Spring on Azure, go to: +- [Spring on Azure](https://docs.microsoft.com/en-us/azure/developer/java/spring-framework/) +- [Azure for Java Cloud Developers](https://docs.microsoft.com/en-us/azure/java/) + +## Credits + +This Spring microservices sample is forked from +[spring-petclinic/spring-petclinic-microservices](https://github.com/spring-petclinic/spring-petclinic-microservices) - see [Petclinic README](./README-petclinic.md). + +## Contributing + +This project welcomes contributions and suggestions. Most contributions require you to agree to a +Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us +the rights to use your contribution. For details, visit https://cla.opensource.microsoft.com. + +When you submit a pull request, a CLA bot will automatically determine whether you need to provide +a CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructions +provided by the bot. You will only need to do this once across all repos using our CLA. + +This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). +For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or +contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. diff --git a/spring-petclinic-microservices/application.yml b/spring-petclinic-microservices/application.yml new file mode 100644 index 000000000..d26d169ec --- /dev/null +++ b/spring-petclinic-microservices/application.yml @@ -0,0 +1,10 @@ +spring: + cloud: + config: + server: + git: + uri: https://github.com/Azure-Samples/spring-petclinic-microservices-config + native: + search-locations: classpath:. + profiles: + active: native diff --git a/spring-petclinic-microservices/docker-compose.yml b/spring-petclinic-microservices/docker-compose.yml new file mode 100644 index 000000000..30ebe3080 --- /dev/null +++ b/spring-petclinic-microservices/docker-compose.yml @@ -0,0 +1,106 @@ +version: '2' + +services: + config-server: + image: springcommunity/spring-petclinic-config-server + container_name: config-server + mem_limit: 512M + ports: + - 8888:8888 + + discovery-server: + image: springcommunity/spring-petclinic-discovery-server + container_name: discovery-server + mem_limit: 512M + depends_on: + - config-server + entrypoint: ["./dockerize","-wait=tcp://config-server:8888","-timeout=60s","--","java", "org.springframework.boot.loader.JarLauncher"] + ports: + - 8761:8761 + + customers-service: + image: springcommunity/spring-petclinic-customers-service + container_name: customers-service + mem_limit: 512M + depends_on: + - config-server + - discovery-server + entrypoint: ["./dockerize","-wait=tcp://discovery-server:8761","-timeout=60s","--","java", "org.springframework.boot.loader.JarLauncher"] + ports: + - 8081:8081 + env_file: + - keyvault.env + + visits-service: + image: springcommunity/spring-petclinic-visits-service + container_name: visits-service + mem_limit: 512M + depends_on: + - config-server + - discovery-server + entrypoint: ["./dockerize","-wait=tcp://discovery-server:8761","-timeout=60s","--","java", "org.springframework.boot.loader.JarLauncher"] + ports: + - 8082:8082 + env_file: + - keyvault.env + + + vets-service: + image: springcommunity/spring-petclinic-vets-service + container_name: vets-service + mem_limit: 512M + depends_on: + - config-server + - discovery-server + entrypoint: ["./dockerize","-wait=tcp://discovery-server:8761","-timeout=60s","--","java", "org.springframework.boot.loader.JarLauncher"] + ports: + - 8083:8083 + env_file: + - keyvault.env + + api-gateway: + image: springcommunity/spring-petclinic-api-gateway + container_name: api-gateway + mem_limit: 512M + depends_on: + - config-server + - discovery-server + entrypoint: ["./dockerize","-wait=tcp://discovery-server:8761","-timeout=60s","--","java", "org.springframework.boot.loader.JarLauncher"] + ports: + - 8080:8080 + + tracing-server: + image: openzipkin/zipkin + container_name: tracing-server + mem_limit: 512M + environment: + - JAVA_OPTS=-XX:+UnlockExperimentalVMOptions -Djava.security.egd=file:/dev/./urandom + ports: + - 9411:9411 + + admin-server: + image: springcommunity/spring-petclinic-admin-server + container_name: admin-server + mem_limit: 512M + depends_on: + - config-server + - discovery-server + entrypoint: ["./dockerize","-wait=tcp://discovery-server:8761","-timeout=60s","--","java", "org.springframework.boot.loader.JarLauncher"] + ports: + - 9090:9090 + + ## Grafana / Prometheus + + grafana-server: + build: ./docker/grafana + container_name: grafana-server + mem_limit: 256M + ports: + - 3000:3000 + + prometheus-server: + build: ./docker/prometheus + container_name: prometheus-server + mem_limit: 256M + ports: + - 9091:9090 diff --git a/spring-petclinic-microservices/docker/Dockerfile b/spring-petclinic-microservices/docker/Dockerfile new file mode 100644 index 000000000..959770892 --- /dev/null +++ b/spring-petclinic-microservices/docker/Dockerfile @@ -0,0 +1,31 @@ +FROM openjdk:11-jre as builder +WORKDIR application +ARG ARTIFACT_NAME +COPY ${ARTIFACT_NAME}.jar application.jar +RUN java -Djarmode=layertools -jar application.jar extract + +# Download dockerize and cache that layer +ARG DOCKERIZE_VERSION +RUN wget -O dockerize.tar.gz https://github.com/jwilder/dockerize/releases/download/${DOCKERIZE_VERSION}/dockerize-alpine-linux-amd64-${DOCKERIZE_VERSION}.tar.gz +RUN tar xzf dockerize.tar.gz +RUN chmod +x dockerize + + +# wget is not installed on adoptopenjdk:11-jre-hotspot +FROM adoptopenjdk:11-jre-hotspot + +WORKDIR application + +# Dockerize +COPY --from=builder application/dockerize ./ + +ARG EXPOSED_PORT +EXPOSE ${EXPOSED_PORT} + +ENV SPRING_PROFILES_ACTIVE docker + +COPY --from=builder application/dependencies/ ./ +COPY --from=builder application/spring-boot-loader/ ./ +COPY --from=builder application/snapshot-dependencies/ ./ +COPY --from=builder application/application/ ./ +ENTRYPOINT ["java", "org.springframework.boot.loader.JarLauncher"] diff --git a/spring-petclinic-microservices/docker/grafana/Dockerfile b/spring-petclinic-microservices/docker/grafana/Dockerfile new file mode 100644 index 000000000..c11b3d911 --- /dev/null +++ b/spring-petclinic-microservices/docker/grafana/Dockerfile @@ -0,0 +1,4 @@ +FROM grafana/grafana:5.2.4 +ADD ./provisioning /etc/grafana/provisioning +ADD ./grafana.ini /etc/grafana/grafana.ini +ADD ./dashboards /var/lib/grafana/dashboards diff --git a/spring-petclinic-microservices/docker/grafana/dashboards/grafana-petclinic-dashboard.json b/spring-petclinic-microservices/docker/grafana/dashboards/grafana-petclinic-dashboard.json new file mode 100644 index 000000000..ccd51d1bf --- /dev/null +++ b/spring-petclinic-microservices/docker/grafana/dashboards/grafana-petclinic-dashboard.json @@ -0,0 +1,772 @@ +{ + "__requires": [ + { + "type": "grafana", + "id": "grafana", + "name": "Grafana", + "version": "5.0.0" + }, + { + "type": "panel", + "id": "graph", + "name": "Graph", + "version": "5.0.0" + }, + { + "type": "datasource", + "id": "prometheus", + "name": "Prometheus", + "version": "5.0.0" + }, + { + "type": "panel", + "id": "singlestat", + "name": "Singlestat", + "version": "5.0.0" + } + ], + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": "-- Grafana --", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "gnetId": null, + "graphTooltip": 0, + "id": null, + "iteration": 1539967676482, + "links": [], + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fill": 1, + "gridPos": { + "h": 9, + "w": 12, + "x": 0, + "y": 0 + }, + "id": 11, + "legend": { + "avg": false, + "current": true, + "max": false, + "min": false, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(http_server_requests_seconds_sum{status!~\"5..\"}[1m]))/sum(rate(http_server_requests_seconds_count{ status!~\"5..\"}[1m]))", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "HTTP - AVG", + "refId": "A" + }, + { + "expr": "max(http_server_requests_seconds_max{status!~\"5..\"})", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "HTTP - MAX", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "HTTP Request Latency", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "decimals": null, + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fill": 1, + "gridPos": { + "h": 9, + "w": 12, + "x": 12, + "y": 0 + }, + "id": 9, + "legend": { + "avg": false, + "current": true, + "max": true, + "min": false, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 0.5, + "points": true, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(http_server_requests_seconds_count[1m]))", + "format": "time_series", + "interval": "", + "intervalFactor": 1, + "legendFormat": "request - ok", + "refId": "A" + }, + { + "expr": "sum(rate(http_server_requests_seconds_count{status=~\"5..\"}[1m]))", + "format": "time_series", + "interval": "", + "intervalFactor": 1, + "legendFormat": "request - err", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "HTTP Request Activity", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "cacheTimeout": null, + "colorBackground": false, + "colorValue": false, + "colors": [ + "#299c46", + "rgba(237, 129, 40, 0.89)", + "#d44a3a" + ], + "datasource": "Prometheus", + "format": "none", + "gauge": { + "maxValue": 100, + "minValue": 0, + "show": false, + "thresholdLabels": false, + "thresholdMarkers": true + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 0, + "y": 9 + }, + "id": 2, + "interval": null, + "links": [], + "mappingType": 1, + "mappingTypes": [ + { + "name": "value to text", + "value": 1 + }, + { + "name": "range to text", + "value": 2 + } + ], + "maxDataPoints": 100, + "nullPointMode": "connected", + "nullText": null, + "postfix": "", + "postfixFontSize": "50%", + "prefix": "", + "prefixFontSize": "50%", + "rangeMaps": [ + { + "from": "null", + "text": "N/A", + "to": "null" + } + ], + "sparkline": { + "fillColor": "rgba(31, 118, 189, 0.18)", + "full": false, + "lineColor": "rgb(31, 120, 193)", + "show": false + }, + "tableColumn": "", + "targets": [ + { + "expr": "sum(petclinic_owner_seconds_count{method=\"PUT\", status=\"204\"})", + "format": "time_series", + "instant": true, + "intervalFactor": 1, + "refId": "A" + } + ], + "thresholds": "", + "title": "Owners Updated", + "type": "singlestat", + "valueFontSize": "80%", + "valueMaps": [ + { + "op": "=", + "text": "N/A", + "value": "null" + } + ], + "valueName": "avg" + }, + { + "cacheTimeout": null, + "colorBackground": false, + "colorValue": false, + "colors": [ + "#299c46", + "rgba(237, 129, 40, 0.89)", + "#d44a3a" + ], + "datasource": "Prometheus", + "format": "none", + "gauge": { + "maxValue": 100, + "minValue": 0, + "show": false, + "thresholdLabels": false, + "thresholdMarkers": true + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 6, + "y": 9 + }, + "id": 3, + "interval": null, + "links": [], + "mappingType": 1, + "mappingTypes": [ + { + "name": "value to text", + "value": 1 + }, + { + "name": "range to text", + "value": 2 + } + ], + "maxDataPoints": 100, + "nullPointMode": "connected", + "nullText": null, + "postfix": "", + "postfixFontSize": "50%", + "prefix": "", + "prefixFontSize": "50%", + "rangeMaps": [ + { + "from": "null", + "text": "N/A", + "to": "null" + } + ], + "sparkline": { + "fillColor": "rgba(31, 118, 189, 0.18)", + "full": false, + "lineColor": "rgb(31, 120, 193)", + "show": false + }, + "tableColumn": "", + "targets": [ + { + "expr": "sum(petclinic_owner_seconds_count{method=\"POST\", status=\"201\"})", + "format": "time_series", + "instant": true, + "intervalFactor": 1, + "legendFormat": "", + "refId": "A" + } + ], + "thresholds": "", + "title": "Owners Created", + "type": "singlestat", + "valueFontSize": "80%", + "valueMaps": [ + { + "op": "=", + "text": "N/A", + "value": "null" + } + ], + "valueName": "avg" + }, + { + "cacheTimeout": null, + "colorBackground": false, + "colorValue": false, + "colors": [ + "#299c46", + "rgba(237, 129, 40, 0.89)", + "#d44a3a" + ], + "datasource": "Prometheus", + "format": "none", + "gauge": { + "maxValue": 100, + "minValue": 0, + "show": false, + "thresholdLabels": false, + "thresholdMarkers": true + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 12, + "y": 9 + }, + "id": 4, + "interval": null, + "links": [], + "mappingType": 1, + "mappingTypes": [ + { + "name": "value to text", + "value": 1 + }, + { + "name": "range to text", + "value": 2 + } + ], + "maxDataPoints": 100, + "nullPointMode": "connected", + "nullText": null, + "postfix": "", + "postfixFontSize": "50%", + "prefix": "", + "prefixFontSize": "50%", + "rangeMaps": [ + { + "from": "null", + "text": "N/A", + "to": "null" + } + ], + "sparkline": { + "fillColor": "rgba(31, 118, 189, 0.18)", + "full": false, + "lineColor": "rgb(31, 120, 193)", + "show": false + }, + "tableColumn": "", + "targets": [ + { + "expr": "sum(petclinic_pet_seconds_count{method=\"POST\", status=\"201\"})", + "format": "time_series", + "instant": true, + "intervalFactor": 1, + "legendFormat": "", + "refId": "A" + } + ], + "thresholds": "", + "title": "Pets Created", + "type": "singlestat", + "valueFontSize": "80%", + "valueMaps": [ + { + "op": "=", + "text": "N/A", + "value": "null" + } + ], + "valueName": "avg" + }, + { + "cacheTimeout": null, + "colorBackground": false, + "colorValue": false, + "colors": [ + "#299c46", + "rgba(237, 129, 40, 0.89)", + "#d44a3a" + ], + "datasource": "Prometheus", + "format": "none", + "gauge": { + "maxValue": 100, + "minValue": 0, + "show": false, + "thresholdLabels": false, + "thresholdMarkers": true + }, + "gridPos": { + "h": 5, + "w": 6, + "x": 18, + "y": 9 + }, + "id": 5, + "interval": null, + "links": [], + "mappingType": 1, + "mappingTypes": [ + { + "name": "value to text", + "value": 1 + }, + { + "name": "range to text", + "value": 2 + } + ], + "maxDataPoints": 100, + "nullPointMode": "connected", + "nullText": null, + "postfix": "", + "postfixFontSize": "50%", + "prefix": "", + "prefixFontSize": "50%", + "rangeMaps": [ + { + "from": "null", + "text": "N/A", + "to": "null" + } + ], + "sparkline": { + "fillColor": "rgba(31, 118, 189, 0.18)", + "full": false, + "lineColor": "rgb(31, 120, 193)", + "show": false + }, + "tableColumn": "Value", + "targets": [ + { + "expr": "sum(petclinic_visit_seconds_count{method=\"POST\", status=\"201\"})", + "format": "time_series", + "instant": true, + "intervalFactor": 1, + "legendFormat": "", + "refId": "A" + } + ], + "thresholds": "", + "title": "Visit Created", + "type": "singlestat", + "valueFontSize": "80%", + "valueMaps": [ + { + "op": "=", + "text": "N/A", + "value": "null" + } + ], + "valueName": "avg" + }, + { + "aliasColors": {}, + "bars": true, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fill": 1, + "gridPos": { + "h": 8, + "w": 24, + "x": 0, + "y": 14 + }, + "id": 7, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "hideEmpty": false, + "hideZero": true, + "max": true, + "min": false, + "rightSide": false, + "show": true, + "total": false, + "values": true + }, + "lines": false, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": true, + "steppedLine": false, + "targets": [ + { + "expr": "sum(petclinic_owner_seconds_count{method=\"POST\", status=\"201\"})", + "format": "time_series", + "instant": false, + "intervalFactor": 1, + "legendFormat": "owner create", + "refId": "A" + }, + { + "expr": "sum(petclinic_pet_seconds_count{method=\"POST\", status=\"201\"})", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "pet create", + "refId": "B" + }, + { + "expr": "sum(petclinic_visit_seconds_count{method=\"POST\", status=\"201\"})", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "visit create", + "refId": "C" + }, + { + "expr": "sum(petclinic_owner_seconds_count{method=\"PUT\", status=\"204\"})", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "owner update", + "refId": "D" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "SPC Business Histogram", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "refresh": "30s", + "schemaVersion": 16, + "style": "dark", + "tags": [], + "templating": { + "list": [ + { + "auto": true, + "auto_count": 1, + "auto_min": "10s", + "current": { + "text": "auto", + "value": "$__auto_interval_timeRange" + }, + "hide": 0, + "label": null, + "name": "timeRange", + "options": [ + { + "selected": true, + "text": "auto", + "value": "$__auto_interval_timeRange" + }, + { + "selected": false, + "text": "1m", + "value": "1m" + }, + { + "selected": false, + "text": "10m", + "value": "10m" + }, + { + "selected": false, + "text": "30m", + "value": "30m" + }, + { + "selected": false, + "text": "1h", + "value": "1h" + }, + { + "selected": false, + "text": "6h", + "value": "6h" + }, + { + "selected": false, + "text": "12h", + "value": "12h" + }, + { + "selected": false, + "text": "1d", + "value": "1d" + }, + { + "selected": false, + "text": "7d", + "value": "7d" + }, + { + "selected": false, + "text": "14d", + "value": "14d" + }, + { + "selected": false, + "text": "30d", + "value": "30d" + } + ], + "query": "1m,10m,30m,1h,6h,12h,1d,7d,14d,30d", + "refresh": 2, + "type": "interval" + } + ] + }, + "time": { + "from": "now-1h", + "to": "now" + }, + "timepicker": { + "refresh_intervals": [ + "5s", + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d" + ], + "time_options": [ + "5m", + "15m", + "1h", + "6h", + "12h", + "24h", + "2d", + "7d", + "30d" + ] + }, + "timezone": "", + "title": "Spring Petclinic Metrics", + "uid": "69JXeR0iw", + "version": 1 +} diff --git a/spring-petclinic-microservices/docker/grafana/grafana.ini b/spring-petclinic-microservices/docker/grafana/grafana.ini new file mode 100644 index 000000000..2919aa463 --- /dev/null +++ b/spring-petclinic-microservices/docker/grafana/grafana.ini @@ -0,0 +1,27 @@ +##################### Spring Petclinic Microservices Grafana Configuration ##################### + +# possible values : production, development +app_mode = development + +#################################### Paths #################################### +[paths] +# folder that contains provisioning config files that grafana will apply on startup and while running. +provisioning = /etc/grafana/provisioning + +#################################### Server #################################### +[server] +# enable gzip +enable_gzip = true + +#################################### Anonymous Auth ########################## +# Anonymous authentication has been enabled in the Petclinic sample with Admin role +# Do not do that in Production environment +[auth.anonymous] +# enable anonymous access +enabled = true + +# specify organization name that should be used for unauthenticated users +org_name = Main Org. + +# specify role for unauthenticated users +org_role = Admin diff --git a/spring-petclinic-microservices/docker/grafana/provisioning/dashboards/all.yml b/spring-petclinic-microservices/docker/grafana/provisioning/dashboards/all.yml new file mode 100644 index 000000000..3b978e625 --- /dev/null +++ b/spring-petclinic-microservices/docker/grafana/provisioning/dashboards/all.yml @@ -0,0 +1,11 @@ +apiVersion: 1 + +providers: +- name: 'default' + orgId: 1 + folder: '' + type: file + disableDeletion: false + updateIntervalSeconds: 10 #how often Grafana will scan for changed dashboards + options: + path: /var/lib/grafana/dashboards diff --git a/spring-petclinic-microservices/docker/grafana/provisioning/datasources/all.yml b/spring-petclinic-microservices/docker/grafana/provisioning/datasources/all.yml new file mode 100644 index 000000000..9c88fce5a --- /dev/null +++ b/spring-petclinic-microservices/docker/grafana/provisioning/datasources/all.yml @@ -0,0 +1,13 @@ +# config file version +apiVersion: 1 + +# list of datasources to insert/update depending what's available in the database +datasources: +- name: Prometheus + type: prometheus + access: proxy + org_id: 1 + url: http://prometheus-server:9090 + is_default: true + version: 1 + editable: true diff --git a/spring-petclinic-microservices/docker/prometheus/Dockerfile b/spring-petclinic-microservices/docker/prometheus/Dockerfile new file mode 100644 index 000000000..58626f638 --- /dev/null +++ b/spring-petclinic-microservices/docker/prometheus/Dockerfile @@ -0,0 +1,2 @@ +FROM prom/prometheus:v2.4.2 +ADD prometheus.yml /etc/prometheus/ diff --git a/spring-petclinic-microservices/docker/prometheus/prometheus.yml b/spring-petclinic-microservices/docker/prometheus/prometheus.yml new file mode 100644 index 000000000..30f089b72 --- /dev/null +++ b/spring-petclinic-microservices/docker/prometheus/prometheus.yml @@ -0,0 +1,32 @@ +# my global config +global: + scrape_interval: 15s # Set the scrape interval to every 15 seconds. Default is every 1 minute. + evaluation_interval: 15s # Evaluate rules every 15 seconds. The default is every 1 minute. + # scrape_timeout is set to the global default (10s). + +# A scrape configuration containing exactly one endpoint to scrape: +# Here it's Prometheus itself. +scrape_configs: +- job_name: prometheus + static_configs: + - targets: ['localhost:9090'] + +- job_name: api-gateway + metrics_path: /actuator/prometheus + static_configs: + - targets: ['api-gateway:8080'] + +- job_name: customers-service + metrics_path: /actuator/prometheus + static_configs: + - targets: ['customers-service:8081'] + +- job_name: visits-service + metrics_path: /actuator/prometheus + static_configs: + - targets: ['visits-service:8082'] + +- job_name: vets-service + metrics_path: /actuator/prometheus + static_configs: + - targets: ['vets-service:8083'] diff --git a/spring-petclinic-microservices/docs/application-screenshot.png b/spring-petclinic-microservices/docs/application-screenshot.png new file mode 100644 index 000000000..e60a403e2 Binary files /dev/null and b/spring-petclinic-microservices/docs/application-screenshot.png differ diff --git a/spring-petclinic-microservices/docs/grafana-custom-metrics-dashboard.png b/spring-petclinic-microservices/docs/grafana-custom-metrics-dashboard.png new file mode 100644 index 000000000..56e87b47e Binary files /dev/null and b/spring-petclinic-microservices/docs/grafana-custom-metrics-dashboard.png differ diff --git a/spring-petclinic-microservices/docs/microservices-architecture-diagram-cosmosdb.jpg b/spring-petclinic-microservices/docs/microservices-architecture-diagram-cosmosdb.jpg new file mode 100644 index 000000000..585e40512 Binary files /dev/null and b/spring-petclinic-microservices/docs/microservices-architecture-diagram-cosmosdb.jpg differ diff --git a/spring-petclinic-microservices/docs/microservices-architecture-diagram.jpg b/spring-petclinic-microservices/docs/microservices-architecture-diagram.jpg new file mode 100644 index 000000000..d22c1cdde Binary files /dev/null and b/spring-petclinic-microservices/docs/microservices-architecture-diagram.jpg differ diff --git a/spring-petclinic-microservices/media/petclinic.jpg b/spring-petclinic-microservices/media/petclinic.jpg new file mode 100644 index 000000000..526012d05 Binary files /dev/null and b/spring-petclinic-microservices/media/petclinic.jpg differ diff --git a/spring-petclinic-microservices/mvnw b/spring-petclinic-microservices/mvnw new file mode 100755 index 000000000..35ff643b3 --- /dev/null +++ b/spring-petclinic-microservices/mvnw @@ -0,0 +1,310 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Maven2 Start Up Batch script +# +# Required ENV vars: +# ------------------ +# JAVA_HOME - location of a JDK home dir +# +# Optional ENV vars +# ----------------- +# M2_HOME - location of maven2's installed home dir +# MAVEN_OPTS - parameters passed to the Java VM when running Maven +# e.g. to debug Maven itself, use +# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +# MAVEN_SKIP_RC - flag to disable loading of mavenrc files +# ---------------------------------------------------------------------------- + +if [ -z "$MAVEN_SKIP_RC" ] ; then + + if [ -f /etc/mavenrc ] ; then + . /etc/mavenrc + fi + + if [ -f "$HOME/.mavenrc" ] ; then + . "$HOME/.mavenrc" + fi + +fi + +# OS specific support. $var _must_ be set to either true or false. +cygwin=false; +darwin=false; +mingw=false +case "`uname`" in + CYGWIN*) cygwin=true ;; + MINGW*) mingw=true;; + Darwin*) darwin=true + # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home + # See https://developer.apple.com/library/mac/qa/qa1170/_index.html + if [ -z "$JAVA_HOME" ]; then + if [ -x "/usr/libexec/java_home" ]; then + export JAVA_HOME="`/usr/libexec/java_home`" + else + export JAVA_HOME="/Library/Java/Home" + fi + fi + ;; +esac + +if [ -z "$JAVA_HOME" ] ; then + if [ -r /etc/gentoo-release ] ; then + JAVA_HOME=`java-config --jre-home` + fi +fi + +if [ -z "$M2_HOME" ] ; then + ## resolve links - $0 may be a link to maven's home + PRG="$0" + + # need this for relative symlinks + while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG="`dirname "$PRG"`/$link" + fi + done + + saveddir=`pwd` + + M2_HOME=`dirname "$PRG"`/.. + + # make it fully qualified + M2_HOME=`cd "$M2_HOME" && pwd` + + cd "$saveddir" + # echo Using m2 at $M2_HOME +fi + +# For Cygwin, ensure paths are in UNIX format before anything is touched +if $cygwin ; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --unix "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --unix "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --unix "$CLASSPATH"` +fi + +# For Mingw, ensure paths are in UNIX format before anything is touched +if $mingw ; then + [ -n "$M2_HOME" ] && + M2_HOME="`(cd "$M2_HOME"; pwd)`" + [ -n "$JAVA_HOME" ] && + JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" +fi + +if [ -z "$JAVA_HOME" ]; then + javaExecutable="`which javac`" + if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then + # readlink(1) is not available as standard on Solaris 10. + readLink=`which readlink` + if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then + if $darwin ; then + javaHome="`dirname \"$javaExecutable\"`" + javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" + else + javaExecutable="`readlink -f \"$javaExecutable\"`" + fi + javaHome="`dirname \"$javaExecutable\"`" + javaHome=`expr "$javaHome" : '\(.*\)/bin'` + JAVA_HOME="$javaHome" + export JAVA_HOME + fi + fi +fi + +if [ -z "$JAVACMD" ] ; then + if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + else + JAVACMD="`which java`" + fi +fi + +if [ ! -x "$JAVACMD" ] ; then + echo "Error: JAVA_HOME is not defined correctly." >&2 + echo " We cannot execute $JAVACMD" >&2 + exit 1 +fi + +if [ -z "$JAVA_HOME" ] ; then + echo "Warning: JAVA_HOME environment variable is not set." +fi + +CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher + +# traverses directory structure from process work directory to filesystem root +# first directory with .mvn subdirectory is considered project base directory +find_maven_basedir() { + + if [ -z "$1" ] + then + echo "Path not specified to find_maven_basedir" + return 1 + fi + + basedir="$1" + wdir="$1" + while [ "$wdir" != '/' ] ; do + if [ -d "$wdir"/.mvn ] ; then + basedir=$wdir + break + fi + # workaround for JBEAP-8937 (on Solaris 10/Sparc) + if [ -d "${wdir}" ]; then + wdir=`cd "$wdir/.."; pwd` + fi + # end of workaround + done + echo "${basedir}" +} + +# concatenates all lines of a file +concat_lines() { + if [ -f "$1" ]; then + echo "$(tr -s '\n' ' ' < "$1")" + fi +} + +BASE_DIR=`find_maven_basedir "$(pwd)"` +if [ -z "$BASE_DIR" ]; then + exit 1; +fi + +########################################################################################## +# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +# This allows using the maven wrapper in projects that prohibit checking in binary data. +########################################################################################## +if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found .mvn/wrapper/maven-wrapper.jar" + fi +else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." + fi + if [ -n "$MVNW_REPOURL" ]; then + jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.4/maven-wrapper-0.5.4.jar" + else + jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.4/maven-wrapper-0.5.4.jar" + fi + while IFS="=" read key value; do + case "$key" in (wrapperUrl) jarUrl="$value"; break ;; + esac + done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" + if [ "$MVNW_VERBOSE" = true ]; then + echo "Downloading from: $jarUrl" + fi + wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" + if $cygwin; then + wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"` + fi + + if command -v wget > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found wget ... using wget" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + wget "$jarUrl" -O "$wrapperJarPath" + else + wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" + fi + elif command -v curl > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found curl ... using curl" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + curl -o "$wrapperJarPath" "$jarUrl" -f + else + curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f + fi + + else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Falling back to using Java to download" + fi + javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" + # For Cygwin, switch paths to Windows format before running javac + if $cygwin; then + javaClass=`cygpath --path --windows "$javaClass"` + fi + if [ -e "$javaClass" ]; then + if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Compiling MavenWrapperDownloader.java ..." + fi + # Compiling the Java class + ("$JAVA_HOME/bin/javac" "$javaClass") + fi + if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + # Running the downloader + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Running MavenWrapperDownloader.java ..." + fi + ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") + fi + fi + fi +fi +########################################################################################## +# End of extension +########################################################################################## + +export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} +if [ "$MVNW_VERBOSE" = true ]; then + echo $MAVEN_PROJECTBASEDIR +fi +MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" + +# For Cygwin, switch paths to Windows format before running java +if $cygwin; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --path --windows "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --windows "$CLASSPATH"` + [ -n "$MAVEN_PROJECTBASEDIR" ] && + MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` +fi + +# Provide a "standardized" way to retrieve the CLI args that will +# work with both Windows and non-Windows executions. +MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" +export MAVEN_CMD_LINE_ARGS + +WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +exec "$JAVACMD" \ + $MAVEN_OPTS \ + -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ + "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ + ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/spring-petclinic-microservices/mvnw.cmd b/spring-petclinic-microservices/mvnw.cmd new file mode 100644 index 000000000..dae46d49c --- /dev/null +++ b/spring-petclinic-microservices/mvnw.cmd @@ -0,0 +1,182 @@ +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM http://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Maven2 Start Up Batch script +@REM +@REM Required ENV vars: +@REM JAVA_HOME - location of a JDK home dir +@REM +@REM Optional ENV vars +@REM M2_HOME - location of maven2's installed home dir +@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending +@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven +@REM e.g. to debug Maven itself, use +@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM ---------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM set title of command window +title %0 +@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' +@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% + +@REM set %HOME% to equivalent of $HOME +if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") + +@REM Execute a user defined script before this one +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre +@REM check for pre script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" +if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" +:skipRcPre + +@setlocal + +set ERROR_CODE=0 + +@REM To isolate internal variables from possible post scripts, we use another setlocal +@setlocal + +@REM ==== START VALIDATION ==== +if not "%JAVA_HOME%" == "" goto OkJHome + +echo. +echo Error: JAVA_HOME not found in your environment. >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +:OkJHome +if exist "%JAVA_HOME%\bin\java.exe" goto init + +echo. +echo Error: JAVA_HOME is set to an invalid directory. >&2 +echo JAVA_HOME = "%JAVA_HOME%" >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +@REM ==== END VALIDATION ==== + +:init + +@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". +@REM Fallback to current working directory if not found. + +set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% +IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir + +set EXEC_DIR=%CD% +set WDIR=%EXEC_DIR% +:findBaseDir +IF EXIST "%WDIR%"\.mvn goto baseDirFound +cd .. +IF "%WDIR%"=="%CD%" goto baseDirNotFound +set WDIR=%CD% +goto findBaseDir + +:baseDirFound +set MAVEN_PROJECTBASEDIR=%WDIR% +cd "%EXEC_DIR%" +goto endDetectBaseDir + +:baseDirNotFound +set MAVEN_PROJECTBASEDIR=%EXEC_DIR% +cd "%EXEC_DIR%" + +:endDetectBaseDir + +IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig + +@setlocal EnableExtensions EnableDelayedExpansion +for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a +@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% + +:endReadAdditionalConfig + +SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" +set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" +set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.4/maven-wrapper-0.5.4.jar" + +FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( + IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B +) + +@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +@REM This allows using the maven wrapper in projects that prohibit checking in binary data. +if exist %WRAPPER_JAR% ( + if "%MVNW_VERBOSE%" == "true" ( + echo Found %WRAPPER_JAR% + ) +) else ( + if not "%MVNW_REPOURL%" == "" ( + SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.4/maven-wrapper-0.5.4.jar" + ) + if "%MVNW_VERBOSE%" == "true" ( + echo Couldn't find %WRAPPER_JAR%, downloading it ... + echo Downloading from: %DOWNLOAD_URL% + ) + + powershell -Command "&{"^ + "$webclient = new-object System.Net.WebClient;"^ + "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ + "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ + "}"^ + "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ + "}" + if "%MVNW_VERBOSE%" == "true" ( + echo Finished downloading %WRAPPER_JAR% + ) +) +@REM End of extension + +@REM Provide a "standardized" way to retrieve the CLI args that will +@REM work with both Windows and non-Windows executions. +set MAVEN_CMD_LINE_ARGS=%* + +%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* +if ERRORLEVEL 1 goto error +goto end + +:error +set ERROR_CODE=1 + +:end +@endlocal & set ERROR_CODE=%ERROR_CODE% + +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost +@REM check for post script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" +if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" +:skipRcPost + +@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' +if "%MAVEN_BATCH_PAUSE%" == "on" pause + +if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% + +exit /B %ERROR_CODE% diff --git a/spring-petclinic-microservices/pom.xml b/spring-petclinic-microservices/pom.xml new file mode 100644 index 000000000..6dd7a4290 --- /dev/null +++ b/spring-petclinic-microservices/pom.xml @@ -0,0 +1,230 @@ + + + 4.0.0 + + + org.springframework.boot + spring-boot-starter-parent + 2.3.6.RELEASE + + + org.springframework.samples + spring-petclinic-microservices + 2.3.6 + ${project.artifactId} + pom + + + spring-petclinic-admin-server + spring-petclinic-customers-service + spring-petclinic-vets-service + spring-petclinic-visits-service + spring-petclinic-config-server + spring-petclinic-discovery-server + spring-petclinic-api-gateway + + + + 1.8 + 3.11.1 + + 2.3.4.RELEASE + Hoxton.SR8 + + 2.22.0 + + springcommunity + 9090 + ${basedir} + v0.6.1 + 1.2.0 + + + + + + org.springframework.cloud + spring-cloud-dependencies + ${spring-cloud.version} + pom + import + + + org.springframework.boot + spring-boot-starter-test + ${spring-boot.version} + test + + + junit + junit + + + + + org.assertj + assertj-core + ${assertj.version} + test + + + + + + + + springboot + + + + src/main/resources/bootstrap.yml + + + + + + org.springframework.boot + spring-boot-maven-plugin + + true + + true + + + + + + + build-info + + + + ${project.build.sourceEncoding} + ${project.reporting.outputEncoding} + ${maven.compiler.source} + ${maven.compiler.target} + + + + + + + + + pl.project13.maven + git-commit-id-plugin + + + + revision + + + + + true + yyyy-MM-dd'T'HH:mm:ssZ + true + ${project.build.outputDirectory}/git.properties + + false + + + + + + + + + buildDocker + + + + + com.spotify + docker-maven-plugin + ${docker.plugin.version} + + + install + + build + + + + + ${docker.image.prefix}/${project.artifactId} + ${docker.image.dockerfile.dir} + docker-hub + https://index.docker.io/v1/ + + + / + ${project.build.directory} + ${project.build.finalName}.jar + + + + ${project.build.finalName} + ${docker.image.exposed.port} + ${docker.image.dockerize.version} + + + + + + + + + + dev + + + env + development + + + + + + cloud + + + env + cloud + + + + + com.microsoft.azure + spring-cloud-starter-azure-spring-cloud-client + 2.2.0 + + + + + + + + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + false + + + + + + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + false + + + + + diff --git a/spring-petclinic-microservices/spring-petclinic-admin-server/pom.xml b/spring-petclinic-microservices/spring-petclinic-admin-server/pom.xml new file mode 100644 index 000000000..626833649 --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-admin-server/pom.xml @@ -0,0 +1,74 @@ + + + 4.0.0 + + org.springframework.samples.petclinic.admin + spring-petclinic-admin-server + jar + Spring Boot Admin server + + + org.springframework.samples + spring-petclinic-microservices + 2.3.6 + + + + 2.2.2 + 9090 + ${basedir}/../docker + + + + + + org.springframework.boot + spring-boot-starter + + + org.springframework.cloud + spring-cloud-starter-config + + + org.springframework.cloud + spring-cloud-starter-netflix-eureka-client + + + + + de.codecentric + spring-boot-admin-starter-server + ${spring-boot-admin.version} + + + de.codecentric + spring-boot-admin-server-ui + ${spring-boot-admin.version} + + + + + org.jolokia + jolokia-core + + + + + + + buildDocker + + + + com.spotify + docker-maven-plugin + ${docker.plugin.version} + + + + + + + diff --git a/spring-petclinic-microservices/spring-petclinic-admin-server/src/main/java/org/springframework/samples/petclinic/admin/SpringBootAdminApplication.java b/spring-petclinic-microservices/spring-petclinic-admin-server/src/main/java/org/springframework/samples/petclinic/admin/SpringBootAdminApplication.java new file mode 100644 index 000000000..7e0429f94 --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-admin-server/src/main/java/org/springframework/samples/petclinic/admin/SpringBootAdminApplication.java @@ -0,0 +1,31 @@ +/* + * Copyright 2002-2017 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.samples.petclinic.admin; + +import de.codecentric.boot.admin.server.config.EnableAdminServer; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.client.discovery.EnableDiscoveryClient; + +@SpringBootApplication +@EnableAdminServer +@EnableDiscoveryClient +public class SpringBootAdminApplication { + public static void main(String[] args) { + SpringApplication.run(SpringBootAdminApplication.class, args); + } + +} diff --git a/spring-petclinic-microservices/spring-petclinic-admin-server/src/main/resources/bootstrap.yml b/spring-petclinic-microservices/spring-petclinic-admin-server/src/main/resources/bootstrap.yml new file mode 100644 index 000000000..690d11c0d --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-admin-server/src/main/resources/bootstrap.yml @@ -0,0 +1,25 @@ +spring: + cloud: + config: + uri: http://localhost:8888 + application: + name: admin-server +--- +spring: + profiles: docker + cloud: + config: + uri: http://config-server:8888 +--- +spring: + profiles: local + cloud: + config: + uri: http://localhost:8888 +eureka: + client: + serviceUrl: + defaultZone: http://localhost:8761/eureka/ +server: + port: 9090 + diff --git a/spring-petclinic-microservices/spring-petclinic-admin-server/src/main/resources/logback-spring.xml b/spring-petclinic-microservices/spring-petclinic-admin-server/src/main/resources/logback-spring.xml new file mode 100644 index 000000000..5d03f7941 --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-admin-server/src/main/resources/logback-spring.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/spring-petclinic-microservices/spring-petclinic-api-gateway/.factorypath b/spring-petclinic-microservices/spring-petclinic-api-gateway/.factorypath new file mode 100644 index 000000000..29bf92844 --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-api-gateway/.factorypath @@ -0,0 +1,159 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/spring-petclinic-microservices/spring-petclinic-api-gateway/.gitignore b/spring-petclinic-microservices/spring-petclinic-api-gateway/.gitignore new file mode 100644 index 000000000..c71ea97ab --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-api-gateway/.gitignore @@ -0,0 +1 @@ +/.apt_generated/ diff --git a/spring-petclinic-microservices/spring-petclinic-api-gateway/pom.xml b/spring-petclinic-microservices/spring-petclinic-api-gateway/pom.xml new file mode 100644 index 000000000..8ab12349a --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-api-gateway/pom.xml @@ -0,0 +1,211 @@ + + + 4.0.0 + + org.springframework.samples.petclinic.api + spring-petclinic-api-gateway + jar + Spring PetClinic API Gateway + + + org.springframework.samples + spring-petclinic-microservices + 2.3.6 + + + + 3.3.7-1 + 1.11.4 + 3.1.1-1 + 1.6.4 + 1.0.3 + 1.8.0 + 8081 + ${basedir}/../docker + + + + + + + org.springframework.boot + spring-boot-devtools + true + + + org.springframework.boot + spring-boot-configuration-processor + true + + + org.springframework.boot + spring-boot-starter-actuator + + + org.springframework.boot + spring-boot-starter-test + test + + + + + org.springframework.cloud + spring-cloud-sleuth-zipkin + + + org.springframework.cloud + spring-cloud-starter-circuitbreaker-reactor-resilience4j + + + org.springframework.cloud + spring-cloud-starter-config + + + org.springframework.cloud + spring-cloud-starter-netflix-eureka-client + + + org.springframework.cloud + spring-cloud-starter-netflix-ribbon + + + org.springframework.cloud + spring-cloud-netflix-ribbon + + + com.netflix.ribbon + ribbon-eureka + + + org.springframework.cloud + spring-cloud-netflix-hystrix + + + + + org.springframework.cloud + spring-cloud-starter-sleuth + + + org.springframework.cloud + spring-cloud-starter-gateway + + + + + org.jolokia + jolokia-core + + + org.projectlombok + lombok + + + io.micrometer + micrometer-registry-prometheus + + + io.github.resilience4j + resilience4j-micrometer + + + + + org.webjars + angularjs + ${webjars-angular.version} + + + org.webjars + jquery + ${webjars-jquery.version} + + + org.webjars + bootstrap + ${webjars-bootstrap.version} + + + org.webjars + angular-ui-router + ${webjars-angular-ui-router.version} + + + org.webjars + webjars-locator-core + + + + + org.junit.jupiter + junit-jupiter-api + test + + + org.junit.jupiter + junit-jupiter-engine + test + + + com.squareup.okhttp3 + mockwebserver + test + + + + + + + ro.isdc.wro4j + wro4j-maven-plugin + ${wro4j.version} + + + generate-resources + + run + + + + + ro.isdc.wro.maven.plugin.manager.factory.ConfigurableWroManagerFactory + ${project.build.directory}/classes/static/css + ${basedir}/src/main/wro/wro.xml + ${basedir}/src/main/wro/wro.properties + ${basedir}/src/main/less + + + + org.webjars + bootstrap + ${webjars-bootstrap.version} + + + + + org.mockito + mockito-core + ${mockito.version} + + + + + + + + + buildDocker + + + + com.spotify + docker-maven-plugin + ${docker.plugin.version} + + + + + + + diff --git a/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/java/org/springframework/samples/petclinic/api/ApiGatewayApplication.java b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/java/org/springframework/samples/petclinic/api/ApiGatewayApplication.java new file mode 100644 index 000000000..fd13cc08d --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/java/org/springframework/samples/petclinic/api/ApiGatewayApplication.java @@ -0,0 +1,90 @@ +/* + * Copyright 2002-2017 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.samples.petclinic.api; + +import io.github.resilience4j.circuitbreaker.CircuitBreakerConfig; +import io.github.resilience4j.timelimiter.TimeLimiterConfig; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.circuitbreaker.resilience4j.ReactiveResilience4JCircuitBreakerFactory; +import org.springframework.cloud.circuitbreaker.resilience4j.Resilience4JConfigBuilder; +import org.springframework.cloud.client.circuitbreaker.Customizer; +import org.springframework.cloud.client.discovery.EnableDiscoveryClient; +import org.springframework.cloud.client.loadbalancer.LoadBalanced; +import org.springframework.context.annotation.Bean; +import org.springframework.core.io.ClassPathResource; +import org.springframework.core.io.Resource; +import org.springframework.http.MediaType; +import org.springframework.web.client.RestTemplate; +import org.springframework.web.reactive.function.client.WebClient; +import org.springframework.web.reactive.function.server.RequestPredicates; +import org.springframework.web.reactive.function.server.RouterFunction; +import org.springframework.web.reactive.function.server.RouterFunctions; +import org.springframework.web.reactive.function.server.ServerResponse; + +import java.time.Duration; + + +/** + * @author Maciej Szarlinski + */ +@EnableDiscoveryClient +@SpringBootApplication +public class ApiGatewayApplication { + + public static void main(String[] args) { + SpringApplication.run(ApiGatewayApplication.class, args); + } + + @Bean + @LoadBalanced + RestTemplate loadBalancedRestTemplate() { + return new RestTemplate(); + } + + @Bean + @LoadBalanced + public WebClient.Builder loadBalancedWebClientBuilder() { + return WebClient.builder(); + } + + @Value("classpath:/static/index.html") + private Resource indexHtml; + + /** + * workaround solution for forwarding to index.html + * @see #9785 + */ + @Bean + RouterFunction routerFunction() { + RouterFunction router = RouterFunctions.resources("/**", new ClassPathResource("static/")) + .andRoute(RequestPredicates.GET("/"), + request -> ServerResponse.ok().contentType(MediaType.TEXT_HTML).bodyValue(indexHtml)); + return router; + } + + /** + * Default Resilience4j circuit breaker configuration + */ + @Bean + public Customizer defaultCustomizer() { + return factory -> factory.configureDefault(id -> new Resilience4JConfigBuilder(id) + .circuitBreakerConfig(CircuitBreakerConfig.ofDefaults()) + .timeLimiterConfig(TimeLimiterConfig.custom().timeoutDuration(Duration.ofSeconds(4)).build()) + .build()); + } +} diff --git a/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/java/org/springframework/samples/petclinic/api/application/CustomersServiceClient.java b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/java/org/springframework/samples/petclinic/api/application/CustomersServiceClient.java new file mode 100644 index 000000000..f3efd6105 --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/java/org/springframework/samples/petclinic/api/application/CustomersServiceClient.java @@ -0,0 +1,41 @@ +/* + * Copyright 2002-2017 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.samples.petclinic.api.application; + +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.samples.petclinic.api.dto.OwnerDetails; +import org.springframework.stereotype.Component; +import org.springframework.web.client.RestTemplate; +import org.springframework.web.reactive.function.client.WebClient; +import reactor.core.publisher.Mono; + +/** + * @author Maciej Szarlinski + */ +@Component +@RequiredArgsConstructor +public class CustomersServiceClient { + + private final WebClient.Builder webClientBuilder; + + public Mono getOwner(final String ownerId) { + return webClientBuilder.build().get() + .uri("http://customers-service/owners/{ownerId}", ownerId) + .retrieve() + .bodyToMono(OwnerDetails.class); + } +} diff --git a/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/java/org/springframework/samples/petclinic/api/application/VisitsServiceClient.java b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/java/org/springframework/samples/petclinic/api/application/VisitsServiceClient.java new file mode 100644 index 000000000..38a2db508 --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/java/org/springframework/samples/petclinic/api/application/VisitsServiceClient.java @@ -0,0 +1,55 @@ +/* + * Copyright 2002-2017 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.samples.petclinic.api.application; + +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.samples.petclinic.api.dto.Visits; +import org.springframework.stereotype.Component; +import org.springframework.web.reactive.function.client.WebClient; +import reactor.core.publisher.Mono; + +import java.util.List; + +import static java.util.stream.Collectors.joining; + +/** + * @author Maciej Szarlinski + */ +@Component +@RequiredArgsConstructor +public class VisitsServiceClient { + + private String hostname = "http://visits-service/"; + + private final WebClient.Builder webClientBuilder; + + public Mono getVisitsForPets(final List petIds) { + return webClientBuilder.build() + .get() + .uri(hostname + "pets/visits?petId={petId}", joinIds(petIds)) + .retrieve() + .bodyToMono(Visits.class); + } + + private String joinIds(List petIds) { + return petIds.stream().map(Object::toString).collect(joining(",")); + } + + void setHostname(String hostname) { + this.hostname = hostname; + } +} diff --git a/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/java/org/springframework/samples/petclinic/api/boundary/web/ApiGatewayController.java b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/java/org/springframework/samples/petclinic/api/boundary/web/ApiGatewayController.java new file mode 100644 index 000000000..adde8d33f --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/java/org/springframework/samples/petclinic/api/boundary/web/ApiGatewayController.java @@ -0,0 +1,77 @@ +/* + * Copyright 2002-2017 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.samples.petclinic.api.boundary.web; + +import lombok.RequiredArgsConstructor; +import org.springframework.cloud.client.circuitbreaker.ReactiveCircuitBreaker; +import org.springframework.cloud.client.circuitbreaker.ReactiveCircuitBreakerFactory; +import org.springframework.samples.petclinic.api.application.CustomersServiceClient; +import org.springframework.samples.petclinic.api.application.VisitsServiceClient; +import org.springframework.samples.petclinic.api.dto.OwnerDetails; +import org.springframework.samples.petclinic.api.dto.Visits; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import reactor.core.publisher.Mono; + +import java.util.function.Function; +import java.util.stream.Collectors; + +/** + * @author Maciej Szarlinski + */ +@RestController +@RequiredArgsConstructor +@RequestMapping("/api/gateway") +public class ApiGatewayController { + + private final CustomersServiceClient customersServiceClient; + + private final VisitsServiceClient visitsServiceClient; + + private final ReactiveCircuitBreakerFactory cbFactory; + + @GetMapping(value = "owners/{ownerId}") + public Mono getOwnerDetails(final @PathVariable String ownerId) { + return customersServiceClient.getOwner(ownerId) + .flatMap(owner -> + visitsServiceClient.getVisitsForPets(owner.getPetIds()) + .transform(it -> { + ReactiveCircuitBreaker cb = cbFactory.create("getOwnerDetails"); + return cb.run(it, throwable -> emptyVisitsForPets()); + }) + .map(addVisitsToOwner(owner)) + ); + + } + + private Function addVisitsToOwner(OwnerDetails owner) { + return visits -> { + owner.getPets() + .forEach(pet -> pet.getVisits() + .addAll(visits.getItems().stream() + .filter(v -> v.getPetId().equals(pet.getId())) + .collect(Collectors.toList())) + ); + return owner; + }; + } + + private Mono emptyVisitsForPets() { + return Mono.just(new Visits()); + } +} diff --git a/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/java/org/springframework/samples/petclinic/api/dto/OwnerDetails.java b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/java/org/springframework/samples/petclinic/api/dto/OwnerDetails.java new file mode 100644 index 000000000..8843ed01e --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/java/org/springframework/samples/petclinic/api/dto/OwnerDetails.java @@ -0,0 +1,52 @@ +/* + * Copyright 2002-2017 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.samples.petclinic.api.dto; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import lombok.Data; + +import java.util.ArrayList; +import java.util.List; + +import static java.util.stream.Collectors.toList; + +/** + * @author Maciej Szarlinski + */ +@Data +public class OwnerDetails { + + private String id; + + private String firstName; + + private String lastName; + + private String address; + + private String city; + + private String telephone; + + private final List pets = new ArrayList<>(); + + @JsonIgnore + public List getPetIds() { + return pets.stream() + .map(PetDetails::getId) + .collect(toList()); + } +} diff --git a/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/java/org/springframework/samples/petclinic/api/dto/PetDetails.java b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/java/org/springframework/samples/petclinic/api/dto/PetDetails.java new file mode 100644 index 000000000..9f5b9342c --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/java/org/springframework/samples/petclinic/api/dto/PetDetails.java @@ -0,0 +1,39 @@ +/* + * Copyright 2002-2017 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.samples.petclinic.api.dto; + +import lombok.Data; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author Maciej Szarlinski + */ +@Data +public class PetDetails { + + private String id; + + private String name; + + private String birthDate; + + private String type; + + private final List visits = new ArrayList<>(); + +} diff --git a/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/java/org/springframework/samples/petclinic/api/dto/PetType.java b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/java/org/springframework/samples/petclinic/api/dto/PetType.java new file mode 100644 index 000000000..63d2b2005 --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/java/org/springframework/samples/petclinic/api/dto/PetType.java @@ -0,0 +1,27 @@ +/* + * Copyright 2002-2017 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.samples.petclinic.api.dto; + +import lombok.Data; + +/** + * @author Maciej Szarlinski + */ +@Data +public class PetType { + + private String name; +} diff --git a/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/java/org/springframework/samples/petclinic/api/dto/VisitDetails.java b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/java/org/springframework/samples/petclinic/api/dto/VisitDetails.java new file mode 100644 index 000000000..a3e1d65ba --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/java/org/springframework/samples/petclinic/api/dto/VisitDetails.java @@ -0,0 +1,35 @@ +/* + * Copyright 2002-2017 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.samples.petclinic.api.dto; + +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * @author Maciej Szarlinski + */ +@Data +@NoArgsConstructor +public class VisitDetails { + + private String id = null; + + private String petId = null; + + private String date = null; + + private String description = null; +} diff --git a/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/java/org/springframework/samples/petclinic/api/dto/Visits.java b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/java/org/springframework/samples/petclinic/api/dto/Visits.java new file mode 100644 index 000000000..90bd82bee --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/java/org/springframework/samples/petclinic/api/dto/Visits.java @@ -0,0 +1,32 @@ +/* + * Copyright 2002-2017 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.samples.petclinic.api.dto; + +import java.util.ArrayList; +import java.util.List; + +import lombok.NoArgsConstructor; +import lombok.Value; + +/** + * @author Maciej Szarlinski + */ +@Value +public class Visits { + + private List items = new ArrayList<>(); + +} diff --git a/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/less/header.less b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/less/header.less new file mode 100644 index 000000000..7cb1a7888 --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/less/header.less @@ -0,0 +1,73 @@ +.navbar { + border-top: 4px solid #6db33f; + background-color: #34302d; + margin-bottom: 0px; + border-bottom: 0; + border-left: 0; + border-right: 0; +} + +.navbar a.navbar-brand { + background: url("../images/spring-logo-dataflow.png") -1px -1px no-repeat; + margin: 12px 0 6px; + width: 229px; + height: 46px; + display: inline-block; + text-decoration: none; + padding: 0; +} + +.navbar a.navbar-brand span { + display: block; + width: 229px; + height: 46px; + background: url("../images/spring-logo-dataflow.png") -1px -48px no-repeat; + opacity: 0; + -moz-transition: opacity 0.12s ease-in-out; + -webkit-transition: opacity 0.12s ease-in-out; + -o-transition: opacity 0.12s ease-in-out; +} + +.navbar a:hover.navbar-brand span { + opacity: 1; +} + +.navbar li > a, .navbar-text { + font-family: "montserratregular", sans-serif; + text-shadow: none; + font-size: 14px; + +/* line-height: 14px; */ + padding: 28px 20px; + transition: all 0.15s; + -webkit-transition: all 0.15s; + -moz-transition: all 0.15s; + -o-transition: all 0.15s; + -ms-transition: all 0.15s; +} + +.navbar li > a { + text-transform: uppercase; +} + +.navbar .navbar-text { + margin-top: 0; + margin-bottom: 0; +} +.navbar li:hover > a { + color: #eeeeee; + background-color: #6db33f; +} + +.navbar-toggle { + border-width: 0; + + .icon-bar + .icon-bar { + margin-top: 3px; + } + .icon-bar { + width: 19px; + height: 3px; + } + +} diff --git a/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/less/petclinic.less b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/less/petclinic.less new file mode 100644 index 000000000..26ff4ca3c --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/less/petclinic.less @@ -0,0 +1,250 @@ +/* + * Copyright 2016 the original author or authors. + * + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +@icon-font-path: "../webjars/bootstrap/fonts/"; + +@spring-green: #6db33f; +@spring-dark-green: #5fa134; +@spring-brown: #34302D; +@spring-grey: #838789; +@spring-light-grey: #f1f1f1; + +@body-bg: @spring-light-grey; +@text-color: @spring-brown; +@link-color: @spring-dark-green; +@link-hover-color: @spring-dark-green; + +@navbar-default-link-color: @spring-light-grey; +@navbar-default-link-active-color: @spring-light-grey; +@navbar-default-link-hover-color: @spring-light-grey; +@navbar-default-link-hover-bg: @spring-green; +@navbar-default-toggle-icon-bar-bg: @spring-light-grey; +@navbar-default-toggle-hover-bg: transparent; +@navbar-default-link-active-bg: @spring-green; + +@border-radius-base: 0; +@border-radius-large: 0; +@border-radius-small: 0; + +@btn-default-color: @spring-light-grey; +@btn-default-bg: @spring-brown; +@btn-default-border: @spring-green; + +@nav-tabs-active-link-hover-color: @spring-light-grey; +@nav-tabs-active-link-hover-bg: @spring-brown; +@nav-tabs-active-link-hover-border-color: @spring-brown; +@nav-tabs-border-color: @spring-brown; + +@pagination-active-bg: @spring-brown; +@pagination-active-border: @spring-green; +@table-border-color: @spring-brown; + +@import "typography.less"; +@import "header.less"; + +.table > thead > tr > th { + background-color: lighten(@spring-brown, 3%); + color: @spring-light-grey; +} + +.table-filter { + background-color: @spring-brown; + padding: 9px 12px; +} + +.nav > li > a { + color: @spring-grey; +} + +.btn-default { + border-width: 2px; + transition: border 0.15s; + -webkit-transition: border 0.15s; + -moz-transition: border 0.15s; + -o-transition: border 0.15s; + -ms-transition: border 0.15s; + + &:hover, + &:focus, + &:active, + &.active, + .open .dropdown-toggle& { + background-color: @spring-brown; + border-color: @spring-brown; + } +} + + +.container .text-muted { + margin: 20px 0; +} + +code { + font-size: 80%; +} + +.xd-container { + margin-top: 40px; + margin-bottom: 100px; + padding-left: 5px; + padding-right: 5px; +} + +h1 { + margin-bottom: 15px +} + +.index-page--subtitle { + font-size: 16px; + line-height: 24px; + margin: 0 0 30px; +} + +.form-horizontal button.btn-inverse { + margin-left: 32px; +} + +#job-params-modal .modal-dialog { + width: 90%; + margin-left:auto; + margin-right:auto; +} + +[ng-cloak].splash { + display: block !important; +} +[ng-cloak] { + display: none; +} + +.splash { + background: @spring-green; + color: @spring-brown; + display: none; +} + +.error-page { + margin-top: 100px; + text-align: center; +} + +.error-page .error-title { + font-size: 24px; + line-height: 24px; + margin: 30px 0 0; +} + +table td { + vertical-align: middle; +} + +table td .progress { + margin-bottom: 0; +} + +table td.action-column { + width: 1px; +} + +.help-block { + color: lighten(@text-color, 50%); // lighten the text some for contrast +} + +.xd-containers { + font-size: 15px; +} + +.cluster-view > table td { + vertical-align: top; +} + +.cluster-view .label, .cluster-view .column-block { + display: block; +} + +.cluster-view .input-group-addon { + width: 0%; +} + +.cluster-view { + margin-bottom: 0; +} + +.deployment-status-deployed { + .label-success; +} + +.deployment-status-incomplete { + .label-warning; +} + +.deployment-status-failed { + .label-danger; +} + +.deployment-status-deploying { + .label-info +} +.deployment-status-na { +} + +.container-details-table th { + background-color: lighten(@spring-brown, 3%); + color: @spring-light-grey; +} + +.status-help-content-table td { + color: @spring-brown; +} + +.alert-success { + .alert-variant(fade(@alert-success-bg, 70%); @alert-success-border; @alert-success-text); +} +.alert-info { + .alert-variant(fade(@alert-info-bg, 70%); @alert-info-border; @alert-info-text); +} +.alert-warning { + .alert-variant(fade(@alert-warning-bg, 70%); @alert-warning-border; @alert-warning-text); +} +.alert-danger { + .alert-variant(fade(@alert-danger-bg, 70%); @alert-danger-border; @alert-danger-text); +} + +.myspinner { + animation-name: spinner; + animation-duration: 2s; + animation-iteration-count: infinite; + animation-timing-function: linear; + + -webkit-transform-origin: 49% 50%; + -webkit-animation-name: spinner; + -webkit-animation-duration: 2s; + -webkit-animation-iteration-count: infinite; + -webkit-animation-timing-function: linear; +} + +@keyframes "spinner" { + from { transform: rotate( 0deg ); } + to { transform: rotate( 360deg ); } +} + +@-webkit-keyframes "spinner" { + from { -webkit-transform: rotate( 0deg ); } + to { -webkit-transform: rotate( 360deg ); } +} + +hr { + border-top: 1px dotted @spring-brown; +} + +@import "responsive.less"; diff --git a/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/less/responsive.less b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/less/responsive.less new file mode 100644 index 000000000..8f3b21545 --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/less/responsive.less @@ -0,0 +1,41 @@ +@media (max-width: 768px) { + .navbar-toggle { + position:absolute; + z-index: 9999; + left:0px; + top:0px; + } + + .navbar a.navbar-brand { + display: block; + margin: 0 auto 0 auto; + width: 148px; + height: 50px; + float: none; + background: url("../images/spring-logo-dataflow-mobile.png") 0 center no-repeat; + } + + .homepage-billboard .homepage-subtitle { + font-size: 21px; + line-height: 21px; + } + + .navbar a.navbar-brand span { + display: none; + } + + .navbar { + border-top-width: 0; + } + + .xd-container { + margin-top: 20px; + margin-bottom: 30px; + } + + .index-page--subtitle { + margin-top: 10px; + margin-bottom: 30px; + } + +} diff --git a/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/less/typography.less b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/less/typography.less new file mode 100644 index 000000000..8b8436e13 --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/less/typography.less @@ -0,0 +1,60 @@ +@font-face { + font-family: 'varela_roundregular'; + + src: url('../fonts/varela_round-webfont.eot'); + src: url('../fonts/varela_round-webfont.eot?#iefix') format('embedded-opentype'), + url('../fonts/varela_round-webfont.woff') format('woff'), + url('../fonts/varela_round-webfont.ttf') format('truetype'), + url('../fonts/varela_round-webfont.svg#varela_roundregular') format('svg'); + font-weight: normal; + font-style: normal; +} + +@font-face { + font-family: 'montserratregular'; + src: url('../fonts/montserrat-webfont.eot'); + src: url('../fonts/montserrat-webfont.eot?#iefix') format('embedded-opentype'), + url('../fonts/montserrat-webfont.woff') format('woff'), + url('../fonts/montserrat-webfont.ttf') format('truetype'), + url('../fonts/montserrat-webfont.svg#montserratregular') format('svg'); + font-weight: normal; + font-style: normal; +} + +body, h1, h2, h3, p, input { + margin: 0; + font-weight: 400; + font-family: "varela_roundregular", sans-serif; + color: #34302d; +} + +h1 { + font-size: 24px; + line-height: 30px; + font-family: "montserratregular", sans-serif; +} + +h2 { + font-size: 18px; + font-weight: 700; + line-height: 24px; + margin-bottom: 10px; + font-family: "montserratregular", sans-serif; +} + +h3 { + font-size: 16px; + line-height: 24px; + margin-bottom: 10px; + font-weight: 700; +} + +p { + //font-size: 15px; + //line-height: 24px; +} + +strong { + font-weight: 700; + font-family: "montserratregular", sans-serif; +} diff --git a/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/application.yml b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/application.yml new file mode 100644 index 000000000..c0f79c463 --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/application.yml @@ -0,0 +1,81 @@ +spring: + cloud: + loadbalancer: + ribbon: + enabled: false + gateway: + routes: + - id: vets-service + uri: lb://vets-service + predicates: + - Path=/api/vet/** + filters: + - StripPrefix=2 + - id: visits-service + uri: lb://visits-service + predicates: + - Path=/api/visit/** + filters: + - StripPrefix=2 + - id: customers-service + uri: lb://customers-service + predicates: + - Path=/api/customer/** + filters: + - StripPrefix=2 +--- +spring: + profiles: docker + cloud: + loadbalancer: + ribbon: + enabled: false + gateway: + routes: + - id: vets-service + uri: lb://vets-service + predicates: + - Path=/api/vet/** + filters: + - StripPrefix=2 + - id: visits-service + uri: lb://visits-service + predicates: + - Path=/api/visit/** + filters: + - StripPrefix=2 + - id: customers-service + uri: lb://customers-service + predicates: + - Path=/api/customer/** + filters: + - StripPrefix=2 + +--- +spring: + profiles: local + cloud: + loadbalancer: + ribbon: + enabled: false + gateway: + routes: + - id: vets-service + uri: http://localhost:8083 + predicates: + - Path=/api/vet/** + filters: + - StripPrefix=2 + - id: visits-service + uri: http://localhost:8082 + predicates: + - Path=/api/visit/** + filters: + - StripPrefix=2 + - id: customers-service + uri: http://localhost:8081 + predicates: + - Path=/api/customer/** + filters: + - StripPrefix=2 + diff --git a/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/bootstrap.yml b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/bootstrap.yml new file mode 100644 index 000000000..3b646f220 --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/bootstrap.yml @@ -0,0 +1,28 @@ +spring: + cloud: + config: + uri: http://localhost:8888 + application: + name: api-gateway +--- +spring: + profiles: docker + cloud: + config: + uri: http://config-server:8888 +server: + port: 8080 +--- +spring: + profiles: local + cloud: + config: + uri: http://localhost:8888 +eureka: + client: + serviceUrl: + defaultZone: http://localhost:8761/eureka/ +server: + port: 8080 + + diff --git a/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/logback-spring.xml b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/logback-spring.xml new file mode 100644 index 000000000..5d03f7941 --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/logback-spring.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/messages/messages.properties b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/messages/messages.properties new file mode 100644 index 000000000..f916c7a4e --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/messages/messages.properties @@ -0,0 +1,7 @@ +required=is required +notFound=has not been found +duplicate=is already in use +nonNumeric=must be all numeric +duplicateFormSubmission=Duplicate form submission is not allowed +typeMismatch.date=invalid date +typeMismatch.birthDate=invalid date diff --git a/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/messages/messages_de.properties b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/messages/messages_de.properties new file mode 100644 index 000000000..46c70b5b0 --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/messages/messages_de.properties @@ -0,0 +1,7 @@ +required=muss angegeben werden +notFound=wurde nicht gefunden +duplicate=ist bereits vergeben +nonNumeric=darf nur numerisch sein +duplicateFormSubmission=Wiederholtes Absenden des Formulars ist nicht erlaubt +typeMismatch.date=ung�ltiges Datum +typeMismatch.birthDate=ung�ltiges Datum diff --git a/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/messages/messages_en.properties b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/messages/messages_en.properties new file mode 100644 index 000000000..05d519bb8 --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/messages/messages_en.properties @@ -0,0 +1 @@ +# This file is intentionally empty. Message look-ups will fall back to the default "messages.properties" file. \ No newline at end of file diff --git a/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/fonts/montserrat-webfont.eot b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/fonts/montserrat-webfont.eot new file mode 100644 index 000000000..0caea9169 Binary files /dev/null and b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/fonts/montserrat-webfont.eot differ diff --git a/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/fonts/montserrat-webfont.svg b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/fonts/montserrat-webfont.svg new file mode 100644 index 000000000..7bd96bdfd --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/fonts/montserrat-webfont.svg @@ -0,0 +1,1283 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/fonts/montserrat-webfont.ttf b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/fonts/montserrat-webfont.ttf new file mode 100644 index 000000000..9953fe62e Binary files /dev/null and b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/fonts/montserrat-webfont.ttf differ diff --git a/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/fonts/montserrat-webfont.woff b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/fonts/montserrat-webfont.woff new file mode 100644 index 000000000..eb49333f7 Binary files /dev/null and b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/fonts/montserrat-webfont.woff differ diff --git a/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/fonts/varela_round-webfont.eot b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/fonts/varela_round-webfont.eot new file mode 100644 index 000000000..dfee0c26e Binary files /dev/null and b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/fonts/varela_round-webfont.eot differ diff --git a/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/fonts/varela_round-webfont.svg b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/fonts/varela_round-webfont.svg new file mode 100644 index 000000000..3280e2c43 --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/fonts/varela_round-webfont.svg @@ -0,0 +1,7875 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/fonts/varela_round-webfont.ttf b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/fonts/varela_round-webfont.ttf new file mode 100644 index 000000000..3ca066693 Binary files /dev/null and b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/fonts/varela_round-webfont.ttf differ diff --git a/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/fonts/varela_round-webfont.woff b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/fonts/varela_round-webfont.woff new file mode 100644 index 000000000..77ba16614 Binary files /dev/null and b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/fonts/varela_round-webfont.woff differ diff --git a/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/images/favicon.png b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/images/favicon.png new file mode 100644 index 000000000..1c649a3cd Binary files /dev/null and b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/images/favicon.png differ diff --git a/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/images/pets.png b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/images/pets.png new file mode 100644 index 000000000..bb5cf3a34 Binary files /dev/null and b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/images/pets.png differ diff --git a/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/images/platform-bg.png b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/images/platform-bg.png new file mode 100644 index 000000000..512185839 Binary files /dev/null and b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/images/platform-bg.png differ diff --git a/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/images/spring-logo-dataflow-mobile.png b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/images/spring-logo-dataflow-mobile.png new file mode 100644 index 000000000..45d24a6f3 Binary files /dev/null and b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/images/spring-logo-dataflow-mobile.png differ diff --git a/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/images/spring-logo-dataflow.png b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/images/spring-logo-dataflow.png new file mode 100644 index 000000000..ff7cdbb40 Binary files /dev/null and b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/images/spring-logo-dataflow.png differ diff --git a/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/images/spring-pivotal-logo.png b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/images/spring-pivotal-logo.png new file mode 100644 index 000000000..1840af274 Binary files /dev/null and b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/images/spring-pivotal-logo.png differ diff --git a/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/index.html b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/index.html new file mode 100644 index 000000000..221feadb6 --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/index.html @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + PetClinic :: a Spring Framework demonstration + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+
+ + + + diff --git a/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/scripts/app.js b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/scripts/app.js new file mode 100644 index 000000000..870aeb209 --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/scripts/app.js @@ -0,0 +1,35 @@ +'use strict'; +/* App Module */ +var petClinicApp = angular.module('petClinicApp', [ + 'ui.router', 'layoutNav', 'layoutFooter', 'layoutWelcome', + 'ownerList', 'ownerDetails', 'ownerForm', 'petForm', 'visits', 'vetList']); + +petClinicApp.config(['$stateProvider', '$urlRouterProvider', '$locationProvider', '$httpProvider', function( + $stateProvider, $urlRouterProvider, $locationProvider, $httpProvider) { + + // safari turns to be lazy sending the Cache-Control header + $httpProvider.defaults.headers.common["Cache-Control"] = 'no-cache'; + + $locationProvider.hashPrefix('!'); + + $urlRouterProvider.otherwise('/welcome'); + $stateProvider + .state('app', { + abstract: true, + url: '', + template: '' + }) + .state('welcome', { + parent: 'app', + url: '/welcome', + template: '' + }); +}]); + +['welcome', 'nav', 'footer'].forEach(function(c) { + var mod = 'layout' + c.toUpperCase().substring(0, 1) + c.substring(1); + angular.module(mod, []); + angular.module(mod).component(mod, { + templateUrl: "scripts/fragments/" + c + ".html" + }); +}); \ No newline at end of file diff --git a/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/scripts/fragments/footer.html b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/scripts/fragments/footer.html new file mode 100644 index 000000000..cc1e7c516 --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/scripts/fragments/footer.html @@ -0,0 +1,6 @@ +
+
+
Sponsored by Pivotal
+
+
\ No newline at end of file diff --git a/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/scripts/fragments/nav.html b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/scripts/fragments/nav.html new file mode 100644 index 000000000..6005cfe3f --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/scripts/fragments/nav.html @@ -0,0 +1,38 @@ + diff --git a/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/scripts/fragments/welcome.html b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/scripts/fragments/welcome.html new file mode 100644 index 000000000..df0b55e9e --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/scripts/fragments/welcome.html @@ -0,0 +1,7 @@ +

Welcome to Petclinic

+ +
+
+ pets logo +
+
\ No newline at end of file diff --git a/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/scripts/owner-details/owner-details.component.js b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/scripts/owner-details/owner-details.component.js new file mode 100644 index 000000000..98a43fcbe --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/scripts/owner-details/owner-details.component.js @@ -0,0 +1,7 @@ +'use strict'; + +angular.module('ownerDetails') + .component('ownerDetails', { + templateUrl: 'scripts/owner-details/owner-details.template.html', + controller: 'OwnerDetailsController' + }); diff --git a/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/scripts/owner-details/owner-details.controller.js b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/scripts/owner-details/owner-details.controller.js new file mode 100644 index 000000000..d164378dc --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/scripts/owner-details/owner-details.controller.js @@ -0,0 +1,10 @@ +'use strict'; + +angular.module('ownerDetails') + .controller('OwnerDetailsController', ['$http', '$stateParams', function ($http, $stateParams) { + var self = this; + + $http.get('api/gateway/owners/' + $stateParams.ownerId).then(function (resp) { + self.owner = resp.data; + }); + }]); diff --git a/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/scripts/owner-details/owner-details.js b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/scripts/owner-details/owner-details.js new file mode 100644 index 000000000..670f24bee --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/scripts/owner-details/owner-details.js @@ -0,0 +1,11 @@ +'use strict'; + +angular.module('ownerDetails', ['ui.router']) + .config(['$stateProvider', function ($stateProvider) { + $stateProvider + .state('ownerDetails', { + parent: 'app', + url: '/owners/details/:ownerId', + template: '' + }) + }]); \ No newline at end of file diff --git a/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/scripts/owner-details/owner-details.template.html b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/scripts/owner-details/owner-details.template.html new file mode 100644 index 000000000..1edb3556d --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/scripts/owner-details/owner-details.template.html @@ -0,0 +1,70 @@ +

Owner Information

+ + + + + + + + + + + + + + + + + + + + + + +
Name{{$ctrl.owner.firstName}} {{$ctrl.owner.lastName}}
Address{{$ctrl.owner.address}}
City{{$ctrl.owner.city}}
Telephone{{$ctrl.owner.telephone}}
+ Edit Owner + + Add New Pet +
+ +

Pets and Visits

+ + + + + + +
+
+
Name
+
{{pet.name}}
+
Birth Date
+
{{pet.birthDate | date:'yyyy MMM dd'}}
+
Type
+
{{pet.type}}
+
+
+ + + + + + + + + + + + + + + +
Visit DateDescription
{{visit.date | date:'yyyy MMM dd'}}{{visit.description}}
+ Edit Pet + + Add Visit +
+
+ + + diff --git a/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/scripts/owner-form/owner-form.component.js b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/scripts/owner-form/owner-form.component.js new file mode 100644 index 000000000..e8dec69c4 --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/scripts/owner-form/owner-form.component.js @@ -0,0 +1,7 @@ +'use strict'; + +angular.module('ownerForm') + .component('ownerForm', { + templateUrl: 'scripts/owner-form/owner-form.template.html', + controller: 'OwnerFormController' + }); diff --git a/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/scripts/owner-form/owner-form.controller.js b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/scripts/owner-form/owner-form.controller.js new file mode 100644 index 000000000..f3e0e057c --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/scripts/owner-form/owner-form.controller.js @@ -0,0 +1,35 @@ +'use strict'; + +angular.module('ownerForm') + .controller('OwnerFormController', ["$http", '$state', '$stateParams', function ($http, $state, $stateParams) { + var self = this; + + var ownerId = $stateParams.ownerId || 0; + + if (!ownerId) { + self.owner = {}; + } else { + $http.get("api/customer/owners/" + ownerId).then(function (resp) { + self.owner = resp.data; + }); + } + + self.submitOwnerForm = function () { + var id = self.owner.id; + var req; + if (id) { + req = $http.put("api/customer/owners/" + id, self.owner); + } else { + req = $http.post("api/customer/owners", self.owner); + } + + req.then(function () { + $state.go('owners'); + }, function (response) { + var error = response.data; + alert(error.error + "\r\n" + error.errors.map(function (e) { + return e.field + ": " + e.defaultMessage; + }).join("\r\n")); + }); + }; + }]); diff --git a/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/scripts/owner-form/owner-form.js b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/scripts/owner-form/owner-form.js new file mode 100644 index 000000000..1bb615913 --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/scripts/owner-form/owner-form.js @@ -0,0 +1,16 @@ +'use strict'; + +angular.module('ownerForm', ['ui.router']) + .config(['$stateProvider', function ($stateProvider) { + $stateProvider + .state('ownerNew', { + parent: 'app', + url: '/owners/new', + template: '' + }) + .state('ownerEdit', { + parent: 'app', + url: '/owners/:ownerId/edit', + template: '' + }) + }]); diff --git a/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/scripts/owner-form/owner-form.template.html b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/scripts/owner-form/owner-form.template.html new file mode 100644 index 000000000..0d49cfb92 --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/scripts/owner-form/owner-form.template.html @@ -0,0 +1,38 @@ +

Owner

+
+
+ + + First name is required. +
+ +
+ + + Last name is required. +
+ + +
+ + + Address is required. +
+ +
+ + + City is required. +
+ +
+ + + Telephone is required. +
+ +
+ +
+
+ diff --git a/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/scripts/owner-list/owner-list.component.js b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/scripts/owner-list/owner-list.component.js new file mode 100644 index 000000000..55b6b253f --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/scripts/owner-list/owner-list.component.js @@ -0,0 +1,7 @@ +'use strict'; + +angular.module('ownerList') + .component('ownerList', { + templateUrl: 'scripts/owner-list/owner-list.template.html', + controller: 'OwnerListController' + }); diff --git a/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/scripts/owner-list/owner-list.controller.js b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/scripts/owner-list/owner-list.controller.js new file mode 100644 index 000000000..8229f43c8 --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/scripts/owner-list/owner-list.controller.js @@ -0,0 +1,10 @@ +'use strict'; + +angular.module('ownerList') + .controller('OwnerListController', ['$http', function ($http) { + var self = this; + + $http.get('api/customer/owners').then(function (resp) { + self.owners = resp.data; + }); + }]); diff --git a/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/scripts/owner-list/owner-list.js b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/scripts/owner-list/owner-list.js new file mode 100644 index 000000000..4714a2f97 --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/scripts/owner-list/owner-list.js @@ -0,0 +1,11 @@ +'use strict'; + +angular.module('ownerList', ['ui.router']) + .config(['$stateProvider', function ($stateProvider) { + $stateProvider + .state('owners', { + parent: 'app', + url: '/owners', + template: '' + }) + }]); \ No newline at end of file diff --git a/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/scripts/owner-list/owner-list.template.html b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/scripts/owner-list/owner-list.template.html new file mode 100644 index 000000000..fc784631a --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/scripts/owner-list/owner-list.template.html @@ -0,0 +1,31 @@ +

Owners

+ +
+
+ +
+
+ + + + + + + + + + + + + + + + + + + +
NameCityTelephone
+ + {{owner.firstName}} {{owner.lastName}} + + {{owner.city}}{{owner.telephone}}
diff --git a/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/scripts/pet-form/pet-form.component.js b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/scripts/pet-form/pet-form.component.js new file mode 100644 index 000000000..6f5e13bb6 --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/scripts/pet-form/pet-form.component.js @@ -0,0 +1,7 @@ +'use strict'; + +angular.module('petForm') + .component('petForm', { + templateUrl: 'scripts/pet-form/pet-form.template.html', + controller: 'PetFormController' + }); diff --git a/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/scripts/pet-form/pet-form.controller.js b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/scripts/pet-form/pet-form.controller.js new file mode 100644 index 000000000..58fe119df --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/scripts/pet-form/pet-form.controller.js @@ -0,0 +1,58 @@ +'use strict'; + +angular.module('petForm') + .controller('PetFormController', ['$http', '$state', '$stateParams', function ($http, $state, $stateParams) { + var self = this; + var ownerId = $stateParams.ownerId || 0; + + $http.get('api/customer/petTypes').then(function (resp) { + self.types = resp.data; + }).then(function () { + + var petId = $stateParams.petId || 0; + + if (petId) { // edit + $http.get("api/customer/owners/" + ownerId + "/pets/" + petId).then(function (resp) { + self.pet = resp.data; + self.pet.birthDate = new Date(self.pet.birthDate); + self.petType = "" + self.pet.type; + }); + } else { + $http.get('api/customer/owners/' + ownerId).then(function (resp) { + self.pet = { + owner: resp.data.firstName + " " + resp.data.lastName + }; + self.petType = ""; + }) + + } + }); + + self.submit = function () { + var id = self.pet.id || 0; + + var data = { + id: id, + name: self.pet.name, + birthDate: self.pet.birthDate, + type: self.petType + }; + + var req; + if (id) { + req = $http.put("api/customer/owners/" + ownerId + "/pets/" + id, data); + } else { + req = $http.post("api/customer/owners/" + ownerId + "/pets", data); + } + + req.then(function () { + $state.go("owners", {ownerId: ownerId}); + }, function (response) { + var error = response.data; + error.errors = error.errors || []; + alert(error.error + "\r\n" + error.errors.map(function (e) { + return e.field + ": " + e.defaultMessage; + }).join("\r\n")); + }); + }; + }]); diff --git a/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/scripts/pet-form/pet-form.js b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/scripts/pet-form/pet-form.js new file mode 100644 index 000000000..70292ad68 --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/scripts/pet-form/pet-form.js @@ -0,0 +1,16 @@ +'use strict'; + +angular.module('petForm', ['ui.router']) + .config(['$stateProvider', function ($stateProvider) { + $stateProvider + .state('petNew', { + parent: 'app', + url: '/owners/:ownerId/new-pet', + template: '' + }) + .state('petEdit', { + parent: 'app', + url: '/owners/:ownerId/pets/:petId', + template: '' + }) + }]); diff --git a/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/scripts/pet-form/pet-form.template.html b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/scripts/pet-form/pet-form.template.html new file mode 100644 index 000000000..80ae3db88 --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/scripts/pet-form/pet-form.template.html @@ -0,0 +1,43 @@ +

Pet

+ +
+
+ +
+

{{$ctrl.pet.owner}}

+
+
+ +
+ +
+ + Name is required. +
+
+ +
+ +
+ + birth date is required. +
+
+ +
+ +
+ +
+
+ +
+
+ +
+
+
diff --git a/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/scripts/vet-list/vet-list.component.js b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/scripts/vet-list/vet-list.component.js new file mode 100644 index 000000000..6d8e95081 --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/scripts/vet-list/vet-list.component.js @@ -0,0 +1,7 @@ +'use strict'; + +angular.module('vetList') + .component('vetList', { + templateUrl: 'scripts/vet-list/vet-list.template.html', + controller: 'VetListController' + }); diff --git a/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/scripts/vet-list/vet-list.controller.js b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/scripts/vet-list/vet-list.controller.js new file mode 100644 index 000000000..4adc7fc18 --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/scripts/vet-list/vet-list.controller.js @@ -0,0 +1,10 @@ +'use strict'; + +angular.module('vetList') + .controller('VetListController', ['$http', function ($http) { + var self = this; + + $http.get('api/vet/vets').then(function (resp) { + self.vetList = resp.data; + }); + }]); diff --git a/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/scripts/vet-list/vet-list.js b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/scripts/vet-list/vet-list.js new file mode 100644 index 000000000..bcecacc0c --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/scripts/vet-list/vet-list.js @@ -0,0 +1,11 @@ +'use strict'; + +angular.module('vetList', ['ui.router']) + .config(['$stateProvider', function ($stateProvider) { + $stateProvider + .state('vets', { + parent: 'app', + url: '/vets', + template: '' + }) + }]); \ No newline at end of file diff --git a/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/scripts/vet-list/vet-list.template.html b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/scripts/vet-list/vet-list.template.html new file mode 100644 index 000000000..e705f0508 --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/scripts/vet-list/vet-list.template.html @@ -0,0 +1,15 @@ +

Veterinarians

+ + + + + + + + + + + + + +
NameSpecialties
{{vet.firstName}} {{vet.lastName}}{{specialty.name + ' '}}
diff --git a/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/scripts/visits/visits.component.js b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/scripts/visits/visits.component.js new file mode 100644 index 000000000..0bfbcad33 --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/scripts/visits/visits.component.js @@ -0,0 +1,7 @@ +'use strict'; + +angular.module('visits') + .component('visits', { + templateUrl: 'scripts/visits/visits.template.html', + controller: 'VisitsController' + }); diff --git a/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/scripts/visits/visits.controller.js b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/scripts/visits/visits.controller.js new file mode 100644 index 000000000..992765831 --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/scripts/visits/visits.controller.js @@ -0,0 +1,30 @@ +'use strict'; + +angular.module('visits') + .controller('VisitsController', ['$http', '$state', '$stateParams', '$filter', function ($http, $state, $stateParams, $filter) { + var self = this; + var petId = $stateParams.petId || 0; + var url = "api/visit/owners/" + ($stateParams.ownerId || 0) + "/pets/" + petId + "/visits"; + self.date = new Date(); + self.desc = ""; + + $http.get(url).then(function (resp) { + self.visits = resp.data; + }); + + self.submit = function () { + var data = { + date: $filter('date')(self.date, "yyyy-MM-dd"), + description: self.desc + }; + + $http.post(url, data).then(function () { + $state.go("owners", { ownerId: $stateParams.ownerId }); + }, function (response) { + var error = response.data; + alert(error.error + "\r\n" + error.errors.map(function (e) { + return e.field + ": " + e.defaultMessage; + }).join("\r\n")); + }); + }; + }]); diff --git a/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/scripts/visits/visits.js b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/scripts/visits/visits.js new file mode 100644 index 000000000..10821acaa --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/scripts/visits/visits.js @@ -0,0 +1,11 @@ +'use strict'; + +angular.module('visits', ['ui.router']) + .config(['$stateProvider', function ($stateProvider) { + $stateProvider + .state('visits', { + parent: 'app', + url: '/owners/:ownerId/pets/:petId/visits', + template: '' + }) + }]); diff --git a/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/scripts/visits/visits.template.html b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/scripts/visits/visits.template.html new file mode 100644 index 000000000..ee099fad5 --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/resources/static/scripts/visits/visits.template.html @@ -0,0 +1,27 @@ +

Visits

+ +
+ +
+ + +
+ +
+ + +
+ +
+ +
+ +
+ +

Previous Visits

+ + + + + +
{{v.date}}{{v.description}}
\ No newline at end of file diff --git a/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/wro/wro.properties b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/wro/wro.properties new file mode 100644 index 000000000..bd6f3a814 --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/wro/wro.properties @@ -0,0 +1,4 @@ +#List of preProcessors +preProcessors=lessCssImport +#List of postProcessors +postProcessors=less4j \ No newline at end of file diff --git a/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/wro/wro.xml b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/wro/wro.xml new file mode 100644 index 000000000..86574683c --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/main/wro/wro.xml @@ -0,0 +1,6 @@ + + + classpath:META-INF/resources/webjars/bootstrap/3.3.7-1/less/bootstrap.less + /petclinic.less + + diff --git a/spring-petclinic-microservices/spring-petclinic-api-gateway/src/test/java/org/springframework/samples/petclinic/api/ApiGatewayApplicationTests.java b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/test/java/org/springframework/samples/petclinic/api/ApiGatewayApplicationTests.java new file mode 100644 index 000000000..864d4fbf3 --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/test/java/org/springframework/samples/petclinic/api/ApiGatewayApplicationTests.java @@ -0,0 +1,15 @@ +package org.springframework.samples.petclinic.api; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.ActiveProfiles; + +@ActiveProfiles("test") +@SpringBootTest +class ApiGatewayApplicationTests { + + @Test + void contextLoads() { + } + +} diff --git a/spring-petclinic-microservices/spring-petclinic-api-gateway/src/test/java/org/springframework/samples/petclinic/api/application/VisitsServiceClientIntegrationTest.java b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/test/java/org/springframework/samples/petclinic/api/application/VisitsServiceClientIntegrationTest.java new file mode 100644 index 000000000..5e8744be4 --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/test/java/org/springframework/samples/petclinic/api/application/VisitsServiceClientIntegrationTest.java @@ -0,0 +1,64 @@ +package org.springframework.samples.petclinic.api.application; + +import okhttp3.mockwebserver.MockResponse; +import okhttp3.mockwebserver.MockWebServer; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.samples.petclinic.api.dto.Visits; +import org.springframework.web.reactive.function.client.WebClient; +import reactor.core.publisher.Mono; + +import java.io.IOException; +import java.util.Collections; +import java.util.function.Consumer; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +class VisitsServiceClientIntegrationTest { + + private static final String PET_ID = "1"; + + private VisitsServiceClient visitsServiceClient; + + private MockWebServer server; + + @BeforeEach + void setUp() { + server = new MockWebServer(); + visitsServiceClient = new VisitsServiceClient(WebClient.builder()); + visitsServiceClient.setHostname(server.url("/").toString()); + } + + @AfterEach + void shutdown() throws IOException { + this.server.shutdown(); + } + + @Test + void getVisitsForPets_withAvailableVisitsService() { + prepareResponse(response -> response + .setHeader("Content-Type", "application/json") + .setBody("{\"items\":[{\"id\":5,\"date\":\"2018-11-15\",\"description\":\"test visit\",\"petId\":1}]}")); + + Mono visits = visitsServiceClient.getVisitsForPets(Collections.singletonList("1")); + + assertVisitDescriptionEquals(visits.block(), PET_ID,"test visit"); + } + + + private void assertVisitDescriptionEquals(Visits visits, String petId, String description) { + assertEquals(1, visits.getItems().size()); + assertNotNull(visits.getItems().get(0)); + assertEquals(petId, visits.getItems().get(0).getPetId()); + assertEquals(description, visits.getItems().get(0).getDescription()); + } + + private void prepareResponse(Consumer consumer) { + MockResponse response = new MockResponse(); + consumer.accept(response); + this.server.enqueue(response); + } + +} diff --git a/spring-petclinic-microservices/spring-petclinic-api-gateway/src/test/java/org/springframework/samples/petclinic/api/boundary/web/ApiGatewayControllerTest.java b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/test/java/org/springframework/samples/petclinic/api/boundary/web/ApiGatewayControllerTest.java new file mode 100644 index 000000000..9ae998463 --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/test/java/org/springframework/samples/petclinic/api/boundary/web/ApiGatewayControllerTest.java @@ -0,0 +1,99 @@ +package org.springframework.samples.petclinic.api.boundary.web; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mockito; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.cloud.circuitbreaker.resilience4j.ReactiveResilience4JAutoConfiguration; +import org.springframework.context.annotation.Import; +import org.springframework.samples.petclinic.api.application.CustomersServiceClient; +import org.springframework.samples.petclinic.api.application.VisitsServiceClient; +import org.springframework.samples.petclinic.api.dto.OwnerDetails; +import org.springframework.samples.petclinic.api.dto.PetDetails; +import org.springframework.samples.petclinic.api.dto.VisitDetails; +import org.springframework.samples.petclinic.api.dto.Visits; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import org.springframework.test.web.reactive.server.WebTestClient; +import reactor.core.publisher.Mono; + +import java.net.ConnectException; +import java.util.Collections; + +@ExtendWith(SpringExtension.class) +@WebFluxTest(controllers = ApiGatewayController.class) +@Import(ReactiveResilience4JAutoConfiguration.class) +class ApiGatewayControllerTest { + + @MockBean + private CustomersServiceClient customersServiceClient; + + @MockBean + private VisitsServiceClient visitsServiceClient; + + @Autowired + private WebTestClient client; + + + @Test + void getOwnerDetails_withAvailableVisitsService() { + OwnerDetails owner = new OwnerDetails(); + PetDetails cat = new PetDetails(); + cat.setId("20"); + cat.setName("Garfield"); + owner.getPets().add(cat); + Mockito + .when(customersServiceClient.getOwner("1")) + .thenReturn(Mono.just(owner)); + + Visits visits = new Visits(); + VisitDetails visit = new VisitDetails(); + visit.setId("300"); + visit.setDescription("First visit"); + visit.setPetId(cat.getId()); + visits.getItems().add(visit); + Mockito + .when(visitsServiceClient.getVisitsForPets(Collections.singletonList(cat.getId()))) + .thenReturn(Mono.just(visits)); + + client.get() + .uri("/api/gateway/owners/1") + .exchange() + .expectStatus().isOk() + //.expectBody(String.class) + //.consumeWith(response -> + // Assertions.assertThat(response.getResponseBody()).isEqualTo("Garfield")); + .expectBody() + .jsonPath("$.pets[0].name").isEqualTo("Garfield") + .jsonPath("$.pets[0].visits[0].description").isEqualTo("First visit"); + } + + /** + * Test Resilience4j fallback method + */ + @Test + void getOwnerDetails_withServiceError() { + OwnerDetails owner = new OwnerDetails(); + PetDetails cat = new PetDetails(); + cat.setId("20"); + cat.setName("Garfield"); + owner.getPets().add(cat); + Mockito + .when(customersServiceClient.getOwner("1")) + .thenReturn(Mono.just(owner)); + + Mockito + .when(visitsServiceClient.getVisitsForPets(Collections.singletonList(cat.getId()))) + .thenReturn(Mono.error(new ConnectException("Simulate error"))); + + client.get() + .uri("/api/gateway/owners/1") + .exchange() + .expectStatus().isOk() + .expectBody() + .jsonPath("$.pets[0].name").isEqualTo("Garfield") + .jsonPath("$.pets[0].visits").isEmpty(); + } + +} diff --git a/spring-petclinic-microservices/spring-petclinic-api-gateway/src/test/jmeter/petclinic_test_plan.jmx b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/test/jmeter/petclinic_test_plan.jmx new file mode 100644 index 000000000..9e88f1a8e --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/test/jmeter/petclinic_test_plan.jmx @@ -0,0 +1,568 @@ + + + + + + false + true + false + + + + PETCLINC_HOST + localhost + = + + + PETCLINIC_PORT + 8080 + = + + + + + + + + + + + ${PETCLINC_HOST} + ${PETCLINIC_PORT} + + + + 6 + + + + + + + + Accept + application/json, text/plain, */* + + + Content-Type + application/json;charset=UTF-8 + + + Accept-Encoding + gzip, deflate, br + + + + + + + + 1 + 0 + 0 + 300 + 30 + + + + false + -1 + + continue + + + + PET_TYPE + + 1 + 6 + + false + + + + 300 + 100.0 + + + + + + + + + + + /api/customer/owners + GET + true + false + true + false + + + + + + + + 200 + + + Assertion.response_code + false + 8 + + + + + true + + + + false + { + "firstName":"Firstname", + "lastName":"Lastname", + "address":"Adress", + "city":"City", + "telephone":"0000000000" +} + = + + + + + + + + /api/customer/owners + POST + true + false + true + false + + + + + + + + 201 + + + Assertion.response_code + false + 8 + + + + OWNER_ID + $.id + + + + + + + + + + + + + /api/gateway/owners/${OWNER_ID} + GET + true + false + true + false + + + + + + + + 200 + + + Assertion.response_code + false + 8 + + + + + true + + + + false + { + "id":"${OWNER_ID}", + "firstName":"Firstname", + "lastName":"Lastname${OWNER_ID}", + "address":"Adress${OWNER_ID}", + "city":"City${OWNER_ID}", + "telephone":"1111111111" +} + = + + + + + + + + /api/customer/owners/${OWNER_ID} + PUT + true + false + true + false + + + + + + + + 204 + + + Assertion.response_code + false + 8 + + + + + true + + + + false + { + "name":"Pet", + "birthDate":"2018-12-31T23:00:00.000Z", + "typeId":"${PET_TYPE}" +} + = + + + + + + + + /api/customer/owners/${OWNER_ID}/pets + POST + true + false + true + false + + + + + + + + 201 + + + Assertion.response_code + false + 8 + + + + PET_ID + $.id + + + + + + 1 + + + + true + + + + false + { + "name":"Pet", + "birthDate":"2018-12-31T23:00:00.000Z", + "typeId":"${PET_TYPE}" +} + = + + + + + + + + /api/customer/owners/${OWNER_ID}/pets + POST + true + false + true + false + + + + + + + + 201 + + + Assertion.response_code + false + 8 + + + + + + true + + + + false + { + "id": ${PET_ID}, + "name":"Pet${OWNER_ID}", + "birthDate":"2018-12-31T23:00:00.000Z", + "typeId":"${PET_TYPE}" +} + = + + + + + + + + /api/customer/owners/${OWNER_ID}/pets/${PET_ID} + PUT + true + false + true + false + + + + + + + + 204 + + + Assertion.response_code + false + 8 + + + + + true + + + + false + { + "date":"2019-03-15", + "description":"Visit" +} + = + + + + + + + + /api/visit/owners/${OWNER_ID}/pets/${PET_ID}/visits + POST + true + false + true + false + + + + + + + + 201 + + + Assertion.response_code + false + 8 + + + + + 1 + + + + true + + + + false + { + "date":"2019-03-15", + "description":"Visit" +} + = + + + + + + + + /api/visit/owners/${OWNER_ID}/pets/${PET_ID}/visits + POST + true + false + true + false + + + + + + + + 201 + + + Assertion.response_code + false + 8 + + + + + + + + + + + + + /api/vet/vets + GET + true + false + true + false + + + + + + + + 200 + + + Assertion.response_code + false + 8 + + + + + false + + saveConfig + + + true + true + true + + true + true + true + true + false + true + true + false + false + false + true + false + false + false + true + 0 + true + true + true + true + true + true + + + + + + + false + + saveConfig + + + true + true + true + + true + true + true + true + false + true + true + false + false + false + true + false + false + false + true + 0 + true + true + true + true + true + true + + + + + + + + + diff --git a/spring-petclinic-microservices/spring-petclinic-api-gateway/src/test/resources/bootstrap-test.yml b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/test/resources/bootstrap-test.yml new file mode 100644 index 000000000..cb8e4f5f7 --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-api-gateway/src/test/resources/bootstrap-test.yml @@ -0,0 +1,2 @@ +spring.cloud.config.enabled: false +eureka.client.enabled: false diff --git a/spring-petclinic-microservices/spring-petclinic-config-server/pom.xml b/spring-petclinic-microservices/spring-petclinic-config-server/pom.xml new file mode 100644 index 000000000..de1e07214 --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-config-server/pom.xml @@ -0,0 +1,69 @@ + + + 4.0.0 + + org.springframework.samples.petclinic.config + spring-petclinic-config-server + jar + Spring PetClinic Config Server + + + org.springframework.samples + spring-petclinic-microservices + 2.3.6 + + + + 8888 + ${basedir}/../docker + + + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + org.springframework.cloud + spring-cloud-config-server + + + + + org.jolokia + jolokia-core + + + + + org.junit.jupiter + junit-jupiter-api + test + + + org.junit.jupiter + junit-jupiter-engine + test + + + + + + buildDocker + + + + com.spotify + docker-maven-plugin + ${docker.plugin.version} + + + + + + diff --git a/spring-petclinic-microservices/spring-petclinic-config-server/src/main/java/org/springframework/samples/petclinic/config/ConfigServerApplication.java b/spring-petclinic-microservices/spring-petclinic-config-server/src/main/java/org/springframework/samples/petclinic/config/ConfigServerApplication.java new file mode 100644 index 000000000..7d91d1012 --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-config-server/src/main/java/org/springframework/samples/petclinic/config/ConfigServerApplication.java @@ -0,0 +1,32 @@ +/* + * Copyright 2002-2017 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.samples.petclinic.config; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.config.server.EnableConfigServer; + +/** + * @author Maciej Szarlinski + */ +@EnableConfigServer +@SpringBootApplication +public class ConfigServerApplication { + + public static void main(String[] args) { + SpringApplication.run(ConfigServerApplication.class, args); + } +} diff --git a/spring-petclinic-microservices/spring-petclinic-config-server/src/main/resources/bootstrap.yml b/spring-petclinic-microservices/spring-petclinic-config-server/src/main/resources/bootstrap.yml new file mode 100644 index 000000000..43e3e9906 --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-config-server/src/main/resources/bootstrap.yml @@ -0,0 +1,11 @@ +server.port: 8888 +spring: + cloud: + config: + server: + git: + uri: https://github.com/spring-petclinic/spring-petclinic-microservices-config + # Use the File System Backend to avoid git pulling. Enable "native" profile in the Config Server. + native: + searchLocations: file:///${GIT_REPO} + diff --git a/spring-petclinic-microservices/spring-petclinic-config-server/src/test/java/org/springframework/samples/petclinic/config/PetclinicConfigServerApplicationTests.java b/spring-petclinic-microservices/spring-petclinic-config-server/src/test/java/org/springframework/samples/petclinic/config/PetclinicConfigServerApplicationTests.java new file mode 100644 index 000000000..e4cd33f3a --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-config-server/src/test/java/org/springframework/samples/petclinic/config/PetclinicConfigServerApplicationTests.java @@ -0,0 +1,28 @@ +/* + * Copyright 2002-2017 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.samples.petclinic.config; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class PetclinicConfigServerApplicationTests { + + @Test + void contextLoads() { + } + +} diff --git a/spring-petclinic-microservices/spring-petclinic-customers-service/pom.xml b/spring-petclinic-microservices/spring-petclinic-customers-service/pom.xml new file mode 100644 index 000000000..b68d3a43b --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-customers-service/pom.xml @@ -0,0 +1,158 @@ + + + 4.0.0 + + org.springframework.samples.petclinic.client + spring-petclinic-customers-service + jar + Spring PetClinic Customers Service + + + org.springframework.samples + spring-petclinic-microservices + 2.3.6 + + + + 8081 + ${basedir}/../docker + + + + + + + org.springframework.boot + spring-boot-starter-actuator + + + org.springframework.boot + spring-boot-starter-test + test + + + org.springframework.boot + spring-boot-starter-web + + + com.azure + azure-spring-data-cosmos + 3.2.0 + + + com.azure.spring + azure-spring-boot-starter-keyvault-secrets + 3.1.0 + + + org.springframework.data + spring-data-rest-core + + + org.springframework.boot + spring-boot-starter-data-redis + + + + + org.springframework.cloud + spring-cloud-starter-config + + + org.springframework.cloud + spring-cloud-starter-netflix-eureka-client + + + org.springframework.cloud + spring-cloud-starter-sleuth + + + org.springframework.cloud + spring-cloud-sleuth-zipkin + + + + + org.springframework.boot + spring-boot-configuration-processor + true + + + org.jolokia + jolokia-core + + + org.projectlombok + lombok + + + io.micrometer + micrometer-registry-prometheus + + + javax.validation + validation-api + + + commons-jxpath + commons-jxpath + 1.3 + compile + + + + + + org.junit.jupiter + junit-jupiter-api + test + + + org.junit.jupiter + junit-jupiter-engine + test + + + org.assertj + assertj-core + test + + + junit + junit + test + + + jakarta.persistence + jakarta.persistence-api + + + + + + + + buildDocker + + + + com.spotify + docker-maven-plugin + ${docker.plugin.version} + + + + + + diff --git a/spring-petclinic-microservices/spring-petclinic-customers-service/src/main/java/org/springframework/samples/petclinic/customers/CustomersServiceApplication.java b/spring-petclinic-microservices/spring-petclinic-customers-service/src/main/java/org/springframework/samples/petclinic/customers/CustomersServiceApplication.java new file mode 100644 index 000000000..b5dc11917 --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-customers-service/src/main/java/org/springframework/samples/petclinic/customers/CustomersServiceApplication.java @@ -0,0 +1,42 @@ +/* + * Copyright 2002-2017 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.samples.petclinic.customers; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cache.annotation.EnableCaching; +import org.springframework.cloud.client.discovery.EnableDiscoveryClient; +// For AAD Sample +//import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; +//import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; + +/** + * @author Maciej Szarlinski + */ + + +// For AAD Sample +//@EnableWebSecurity +//@EnableGlobalMethodSecurity(prePostEnabled = true) +@EnableCaching +@EnableDiscoveryClient +@SpringBootApplication +public class CustomersServiceApplication { + + public static void main(String[] args) { + SpringApplication.run(CustomersServiceApplication.class, args); + } +} diff --git a/spring-petclinic-microservices/spring-petclinic-customers-service/src/main/java/org/springframework/samples/petclinic/customers/PopulateSeedData.java b/spring-petclinic-microservices/spring-petclinic-customers-service/src/main/java/org/springframework/samples/petclinic/customers/PopulateSeedData.java new file mode 100644 index 000000000..ebd8e7d1f --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-customers-service/src/main/java/org/springframework/samples/petclinic/customers/PopulateSeedData.java @@ -0,0 +1,98 @@ +package org.springframework.samples.petclinic.customers; + +import com.azure.cosmos.implementation.guava25.collect.Lists; +import lombok.SneakyThrows; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.samples.petclinic.customers.model.Owner; +import org.springframework.samples.petclinic.customers.model.OwnerRepository; +import org.springframework.samples.petclinic.customers.model.Pet; +import org.springframework.samples.petclinic.customers.model.PetRepository; +import org.springframework.stereotype.Component; +import reactor.core.publisher.Flux; + +import javax.annotation.PostConstruct; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.HashSet; +import java.util.Set; + + +@Component +public class PopulateSeedData { + + private static final Logger logger = LoggerFactory.getLogger(PopulateSeedData.class); + + @Autowired + private OwnerRepository ownerRepository; + + @Autowired + private PetRepository petRepository; + + + @PostConstruct + public void populateSeedData() { + + final Pet pet1 = new Pet("1", "Leo", parseDate("2000-09-07"), "cat"); + final Pet pet2 = new Pet("2", "Basil", parseDate("2002-08-06"), "hamster"); + final Pet pet3 = new Pet("3", "Rosy", parseDate("2001-04-17"), "dog"); + final Pet pet4 = new Pet("4", "Jewel", parseDate("2000-03-07"), "dog"); + final Pet pet5 = new Pet("5", "Iggy", parseDate("2000-11-30"), "lizard"); + final Pet pet6 = new Pet("6", "George", parseDate("2000-01-20"), "snake"); + final Pet pet7 = new Pet("7", "Samantha", parseDate("1995-09-04"), "cat"); + final Pet pet8 = new Pet("8", "Max", parseDate("1995-09-04"), "cat"); + final Pet pet9 = new Pet("9", "Lucky", parseDate("1999-08-06"), "bird"); + final Pet pet10 = new Pet("10", "Mulligan", parseDate("1997-02-24"), "dog"); + final Pet pet11 = new Pet("11", "Freddy", parseDate("2000-03-09"), "bird"); + final Pet pet12 = new Pet("12", "Lucky", parseDate("2000-06-24"), "dog"); + final Pet pet13 = new Pet("13", "Sly", parseDate("2002-06-08"), "cat"); + + final Owner owner1 = new Owner("1", "George", "Franklin", "110 W. Liberty St.", "Madison", "6085551023",new HashSet() {{ + add(pet1); + }}); + final Owner owner2 = new Owner("2", "Betty", "Davis", "638 Cardinal Ave.", "Sun Prairie", "6085551749", new HashSet() {{ + add(pet2); + }}); + final Owner owner3 = new Owner("3", "Eduardo", "Rodriquez", "2693 Commerce St.", "McFarland", "6085558763", new HashSet() {{ + add(pet3); + add(pet4); + }}); + final Owner owner4 = new Owner("4", "Harold", "Davis", "563 Friendly St.", "Windsor", "6085553198", new HashSet() {{ + add(pet5); + }}); + final Owner owner5 = new Owner("5", "Peter", "McTavish", "2387 S. Fair Way", "Madison", "6085552765", new HashSet() {{ + add(pet6); + }}); + final Owner owner6 = new Owner("6", "Jean", "Coleman", "105 N. Lake St.", "Monona", "6085552654", new HashSet() {{ + add(pet7); + add(pet8); + }}); + final Owner owner7 = new Owner("7", "Jeff", "Black", "1450 Oak Blvd.", "Monona", "6085555387", new HashSet() {{ + add(pet9); + }}); + final Owner owner8 = new Owner("8", "Maria", "Escobito", "345 Maple St.", "Madison", "6085557683", new HashSet() {{ + add(pet10); + }}); + final Owner owner9 = new Owner("9", "David", "Schroeder", "2749 Blackhawk Trail", "Madison", "6085559435", new HashSet() {{ + add(pet11); + }}); + final Owner owner10 = new Owner("10", "Carlos", "Estaban", "2335 Independence La.", "Waunakee", "6085555487", new HashSet() {{ + add(pet12); + add(pet13); + }}); + + + this.ownerRepository.saveAll(Lists.newArrayList(owner1, owner2, owner3, owner4, owner5, owner6, owner7, owner8, owner9, owner10)); + this.petRepository.saveAll(Lists.newArrayList(pet1, pet2, pet3, pet4, pet5, pet6, pet7, pet8, pet9, pet10, pet11, pet12, pet13)); + } + + public static Date parseDate(String date) { + try { + return new SimpleDateFormat("yyyy-MM-dd").parse(date); + } catch (ParseException e) { + return null; + } + } +} diff --git a/spring-petclinic-microservices/spring-petclinic-customers-service/src/main/java/org/springframework/samples/petclinic/customers/model/CosmosProperties.java b/spring-petclinic-microservices/spring-petclinic-customers-service/src/main/java/org/springframework/samples/petclinic/customers/model/CosmosProperties.java new file mode 100644 index 000000000..1e4c95777 --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-customers-service/src/main/java/org/springframework/samples/petclinic/customers/model/CosmosProperties.java @@ -0,0 +1,22 @@ +package org.springframework.samples.petclinic.customers.model; + +import lombok.Getter; +import lombok.Setter; +import org.springframework.boot.context.properties.ConfigurationProperties; + + +@Getter +@Setter +@ConfigurationProperties(prefix = "azure.cosmosdb") +public class CosmosProperties { + + private String uri; + + private String key; + + private String secondaryKey; + + private String database; + + private boolean populateQueryMetrics; +} diff --git a/spring-petclinic-microservices/spring-petclinic-customers-service/src/main/java/org/springframework/samples/petclinic/customers/model/CustomersAppConfiguration.java b/spring-petclinic-microservices/spring-petclinic-customers-service/src/main/java/org/springframework/samples/petclinic/customers/model/CustomersAppConfiguration.java new file mode 100644 index 000000000..9c6f128c6 --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-customers-service/src/main/java/org/springframework/samples/petclinic/customers/model/CustomersAppConfiguration.java @@ -0,0 +1,79 @@ +package org.springframework.samples.petclinic.customers.model; +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +/** + * WARNING: MODIFYING THIS FILE WILL REQUIRE CORRESPONDING UPDATES TO README.md FILE. LINE NUMBERS + * ARE USED TO EXTRACT APPROPRIATE CODE SEGMENTS FROM THIS FILE. ADD NEW CODE AT THE BOTTOM TO AVOID CHANGING + * LINE NUMBERS OF EXISTING CODE SAMPLES. + */ + +import com.azure.core.credential.AzureKeyCredential; +import com.azure.cosmos.CosmosClientBuilder; +import com.azure.cosmos.DirectConnectionConfig; +import com.azure.cosmos.GatewayConnectionConfig; +import com.azure.spring.data.cosmos.config.AbstractCosmosConfiguration; +import com.azure.spring.data.cosmos.config.CosmosConfig; +import com.azure.spring.data.cosmos.core.ResponseDiagnostics; +import com.azure.spring.data.cosmos.core.ResponseDiagnosticsProcessor; +import com.azure.spring.data.cosmos.repository.config.EnableCosmosRepositories; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.PropertySource; +import org.springframework.lang.Nullable; +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +@Configuration +@EnableConfigurationProperties(CosmosProperties.class) +@EnableCosmosRepositories +@PropertySource("classpath:application.properties") +public class CustomersAppConfiguration extends AbstractCosmosConfiguration { + + private static final Logger logger = LoggerFactory.getLogger(CustomersAppConfiguration.class); + + @Autowired + private CosmosProperties properties; + + private AzureKeyCredential azureKeyCredential; + + @Bean + public CosmosClientBuilder cosmosClientBuilder() { + this.azureKeyCredential = new AzureKeyCredential(properties.getKey()); + DirectConnectionConfig directConnectionConfig = new DirectConnectionConfig(); + GatewayConnectionConfig gatewayConnectionConfig = new GatewayConnectionConfig(); + return new CosmosClientBuilder() + .endpoint(properties.getUri()) + .credential(azureKeyCredential) + .directMode(directConnectionConfig, gatewayConnectionConfig); + } + + @Override + public CosmosConfig cosmosConfig() { + return CosmosConfig.builder() + .enableQueryMetrics(properties.isPopulateQueryMetrics()) + .responseDiagnosticsProcessor(new ResponseDiagnosticsProcessorImplementation()) + .build(); + } + + + public void switchToSecondaryKey() { + this.azureKeyCredential.update(properties.getSecondaryKey()); + } + + @Override + protected String getDatabaseName() { + return properties.getDatabase(); + } + + private static class ResponseDiagnosticsProcessorImplementation implements ResponseDiagnosticsProcessor { + + @Override + public void processResponseDiagnostics(@Nullable ResponseDiagnostics responseDiagnostics) { + logger.info("Response Diagnostics {}", responseDiagnostics); + } + } +} diff --git a/spring-petclinic-microservices/spring-petclinic-customers-service/src/main/java/org/springframework/samples/petclinic/customers/model/Owner.java b/spring-petclinic-microservices/spring-petclinic-customers-service/src/main/java/org/springframework/samples/petclinic/customers/model/Owner.java new file mode 100644 index 000000000..498a197a3 --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-customers-service/src/main/java/org/springframework/samples/petclinic/customers/model/Owner.java @@ -0,0 +1,145 @@ +/* + * Copyright 2002-2017 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.samples.petclinic.customers.model; + +import com.azure.spring.data.cosmos.core.mapping.Container; +import com.azure.spring.data.cosmos.core.mapping.GeneratedValue; +import com.azure.spring.data.cosmos.core.mapping.PartitionKey; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import net.minidev.json.annotate.JsonIgnore; +import org.springframework.beans.support.MutableSortDefinition; +import org.springframework.beans.support.PropertyComparator; +import org.springframework.core.style.ToStringCreator; +import org.springframework.data.annotation.Id; + +import javax.validation.constraints.Digits; +import javax.validation.constraints.NotEmpty; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * Simple JavaBean domain object representing an owner. + * + * @author Ken Krebs + * @author Juergen Hoeller + * @author Sam Brannen + * @author Michael Isvy + * @author Maciej Szarlinski + */ +@Container(containerName = "owners") +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor +public class Owner implements Serializable { + + @Id + @GeneratedValue + private String id; + + private String firstName; + + @PartitionKey + private String lastName; + + private String address; + + private String city; + + private String telephone; + + private Set pets; + + public String getId() { + return id; + } + + public String getFirstName() { + return firstName; + } + + public void setFirstName(final String firstName) { + this.firstName = firstName; + } + + public String getLastName() { + return lastName; + } + + public void setLastName(final String lastName) { + this.lastName = lastName; + } + + public String getAddress() { + return this.address; + } + + public void setAddress(String address) { + this.address = address; + } + + public String getCity() { + return this.city; + } + + public void setCity(String city) { + this.city = city; + } + + public String getTelephone() { + return this.telephone; + } + + public void setTelephone(String telephone) { + this.telephone = telephone; + } + + protected Set getPetsInternal() { + if (this.pets == null) { + this.pets = new HashSet<>(); + } + return this.pets; + } + + public List getPets() { + final List sortedPets = new ArrayList<>(getPetsInternal()); + PropertyComparator.sort(sortedPets, new MutableSortDefinition("name", true, true)); + return Collections.unmodifiableList(sortedPets); + } + + public void addPet(Pet pet) { + getPetsInternal().add(pet); + } + + @Override + public String toString() { + return new ToStringCreator(this) + .append("id", this.getId()) + .append("lastName", this.getLastName()) + .append("firstName", this.getFirstName()) + .append("address", this.address) + .append("city", this.city) + .append("telephone", this.telephone) + .toString(); + } +} diff --git a/spring-petclinic-microservices/spring-petclinic-customers-service/src/main/java/org/springframework/samples/petclinic/customers/model/OwnerRepository.java b/spring-petclinic-microservices/spring-petclinic-customers-service/src/main/java/org/springframework/samples/petclinic/customers/model/OwnerRepository.java new file mode 100644 index 000000000..f7fd9f6d4 --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-customers-service/src/main/java/org/springframework/samples/petclinic/customers/model/OwnerRepository.java @@ -0,0 +1,33 @@ +/* + * Copyright 2002-2017 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.samples.petclinic.customers.model; + +import com.azure.spring.data.cosmos.repository.CosmosRepository; +import org.springframework.stereotype.Repository; + +/** + * Repository class for Owner domain objects All method names are compliant with Spring Data naming + * conventions so this interface can easily be extended for Spring Data See here: http://static.springsource.org/spring-data/jpa/docs/current/reference/html/jpa.repositories.html#jpa.query-methods.query-creation + * + * @author Ken Krebs + * @author Juergen Hoeller + * @author Sam Brannen + * @author Michael Isvy + * @author Maciej Szarlinski + */ +@Repository +public interface OwnerRepository extends CosmosRepository { +} diff --git a/spring-petclinic-microservices/spring-petclinic-customers-service/src/main/java/org/springframework/samples/petclinic/customers/model/Pet.java b/spring-petclinic-microservices/spring-petclinic-customers-service/src/main/java/org/springframework/samples/petclinic/customers/model/Pet.java new file mode 100644 index 000000000..8e8925a17 --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-customers-service/src/main/java/org/springframework/samples/petclinic/customers/model/Pet.java @@ -0,0 +1,108 @@ +/* + * Copyright 2002-2017 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.samples.petclinic.customers.model; + +import com.azure.spring.data.cosmos.core.mapping.Container; +import com.azure.spring.data.cosmos.core.mapping.GeneratedValue; +import com.fasterxml.jackson.annotation.JsonIgnore; +import lombok.*; +import org.springframework.core.style.ToStringCreator; +import org.springframework.data.annotation.Id; + +import java.io.Serializable; +import java.util.Date; + +/** + * Simple business object representing a pet. + * + * @author Ken Krebs + * @author Juergen Hoeller + * @author Sam Brannen + * @author Maciej Szarlinski + */ +@Container(containerName = "pets") +@Builder(builderMethodName = "pet") +@AllArgsConstructor +@NoArgsConstructor +public class Pet implements Serializable { + + @Id + @GeneratedValue + private String id; + + private String name; + + private Date birthDate; + + private String petType; + +// @JsonIgnore +// private Owner owner; + + public String getId() { + return id; + } + + public void setId(final String id) { + this.id = id; + } + + public String getName() { + return this.name; + } + + public void setName(final String name) { + this.name = name; + } + + public Date getBirthDate() { + return birthDate; + } + + public void setBirthDate(final Date birthDate) { + this.birthDate = birthDate; + } + + public String getType() { + return petType; + } + + public void setType(final String type) { + this.petType = type; + } + +// public Owner getOwner() { +// return owner; +// } +// +// public void setOwner(final Owner owner) { +// this.owner = owner; +// } + + @Override + public String toString() { + return new ToStringCreator(this) + .append("id", this.getId()) + .append("name", this.getName()) + .append("birthDate", this.getBirthDate()) + .append("type", this.getType() + ) +// .append("ownerFirstname", this.getOwner().getFirstName()) +// .append("ownerLastname", this.getOwner().getLastName()) + .toString(); + } + +} diff --git a/spring-petclinic-microservices/spring-petclinic-customers-service/src/main/java/org/springframework/samples/petclinic/customers/model/PetRepository.java b/spring-petclinic-microservices/spring-petclinic-customers-service/src/main/java/org/springframework/samples/petclinic/customers/model/PetRepository.java new file mode 100644 index 000000000..38a3fd435 --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-customers-service/src/main/java/org/springframework/samples/petclinic/customers/model/PetRepository.java @@ -0,0 +1,38 @@ +/* + * Copyright 2002-2017 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.samples.petclinic.customers.model; +import com.azure.spring.data.cosmos.repository.CosmosRepository; +import com.azure.spring.data.cosmos.repository.Query; +import org.springframework.stereotype.Repository; + +import java.util.List; + +/** + * Repository class for Pet domain objects All method names are compliant with Spring Data naming + * conventions so this interface can easily be extended for Spring Data See here: http://static.springsource.org/spring-data/jpa/docs/current/reference/html/jpa.repositories.html#jpa.query-methods.query-creation + * + * @author Ken Krebs + * @author Juergen Hoeller + * @author Sam Brannen + * @author Michael Isvy + * @author Maciej Szarlinski + */ +@Repository +public interface PetRepository extends CosmosRepository { + @Query(value = "select DISTINCT VALUE p.type from Pet p") + List getPetTypes(); +} + diff --git a/spring-petclinic-microservices/spring-petclinic-customers-service/src/main/java/org/springframework/samples/petclinic/customers/web/OwnerResource.java b/spring-petclinic-microservices/spring-petclinic-customers-service/src/main/java/org/springframework/samples/petclinic/customers/web/OwnerResource.java new file mode 100644 index 000000000..899693971 --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-customers-service/src/main/java/org/springframework/samples/petclinic/customers/web/OwnerResource.java @@ -0,0 +1,128 @@ +/* + * Copyright 2002-2017 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.samples.petclinic.customers.web; + +import io.micrometer.core.annotation.Timed; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.http.HttpStatus; +import org.springframework.samples.petclinic.customers.model.Owner; +import org.springframework.samples.petclinic.customers.model.OwnerRepository; +import org.springframework.samples.petclinic.customers.model.Pet; +import org.springframework.web.bind.annotation.*; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; +// Required for AAD starter to work. For sample only +//import org.springframework.security.access.prepost.PreAuthorize; + + +import javax.validation.Valid; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +/** + * @author Juergen Hoeller + * @author Ken Krebs + * @author Arjen Poutsma + * @author Michael Isvy + * @author Maciej Szarlinski + */ +// For AAD sample +//@PreAuthorize("hasRole('ROLE_owners')") +@RequestMapping("/owners") +@RestController +@Timed("petclinic.owner") +@RequiredArgsConstructor +@Slf4j +class OwnerResource { + + private final OwnerRepository ownerRepository; + + /** + * Create Owner + * + * @return + */ + @PostMapping + @ResponseStatus(HttpStatus.CREATED) + public Owner createOwner(@Valid @RequestBody Owner owner) { + return ownerRepository.save(owner); + } + + /** + * Read single Owner + * + * @return + */ + @GetMapping(value = "/{ownerId}") + public Optional findOwner(@PathVariable("ownerId") String ownerId) { + return ownerRepository.findById(ownerId); + } + + /** + * Read List of Owners + */ + @GetMapping + @Cacheable("owners") + public List findAll() { + List list = new ArrayList<>(); + ownerRepository.findAll().forEach(list::add); + return list; + } + + /** + * Clears the cache for all Owners + */ + @GetMapping(value = "/clearcache") + @CacheEvict("owners") + @ResponseStatus(HttpStatus.NO_CONTENT) + public void clearAllCache() { + } + + /** + * Read List of Owners + */ + @GetMapping(value = "/{ownerId}/pets") + public List findAllPets(@PathVariable("ownerId") String ownerId) { + Optional optionalOwner = ownerRepository.findById(ownerId); + List list = new ArrayList<>(); + if(optionalOwner.isPresent()) { + list = optionalOwner.get().getPets(); + } + return list; + } + + /** + * Update Owner + */ + @PutMapping(value = "/{ownerId}") + @ResponseStatus(HttpStatus.NO_CONTENT) + public void updateOwner(@PathVariable("ownerId") String ownerId, @Valid @RequestBody Owner ownerRequest) { + Optional ownerOptional = ownerRepository.findById(ownerId); + ownerOptional.ifPresent(owner -> { + owner.setFirstName(ownerRequest.getFirstName()); + owner.setLastName(ownerRequest.getLastName()); + owner.setCity(ownerRequest.getCity()); + owner.setAddress(ownerRequest.getAddress()); + owner.setTelephone(ownerRequest.getTelephone()); + log.info("Saving owner {}", owner); + ownerRepository.save(owner); + }); + } +} diff --git a/spring-petclinic-microservices/spring-petclinic-customers-service/src/main/java/org/springframework/samples/petclinic/customers/web/PetDetails.java b/spring-petclinic-microservices/spring-petclinic-customers-service/src/main/java/org/springframework/samples/petclinic/customers/web/PetDetails.java new file mode 100644 index 000000000..94accb790 --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-customers-service/src/main/java/org/springframework/samples/petclinic/customers/web/PetDetails.java @@ -0,0 +1,52 @@ +/* + * Copyright 2002-2017 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.samples.petclinic.customers.web; + +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; +import org.springframework.samples.petclinic.customers.model.Pet; +import reactor.core.publisher.Mono; + +import java.util.Date; +import java.util.Optional; + +/** + * @author mszarlinski@bravurasolutions.com on 2016-12-05. + */ +@Data +class PetDetails { + + private String id; + + private String name; + +// private String owner; + + @DateTimeFormat(pattern = "yyyy-MM-dd") + private Date birthDate; + + private String type; + + PetDetails(Optional petInfo) { + petInfo.ifPresent(pet -> { + this.id = pet.getId(); + this.name = pet.getName(); + // this.owner = pet.getOwner().getFirstName() + " " + pet.getOwner().getLastName(); + this.birthDate = pet.getBirthDate(); + this.type = pet.getType(); + }); + } +} diff --git a/spring-petclinic-microservices/spring-petclinic-customers-service/src/main/java/org/springframework/samples/petclinic/customers/web/PetRequest.java b/spring-petclinic-microservices/spring-petclinic-customers-service/src/main/java/org/springframework/samples/petclinic/customers/web/PetRequest.java new file mode 100644 index 000000000..fda5af0b8 --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-customers-service/src/main/java/org/springframework/samples/petclinic/customers/web/PetRequest.java @@ -0,0 +1,40 @@ +/* + * Copyright 2002-2017 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.samples.petclinic.customers.web; + +import lombok.Data; + +import java.util.Date; + +import javax.validation.constraints.Size; + +import com.fasterxml.jackson.annotation.JsonFormat; + +/** + * @author mszarlinski@bravurasolutions.com on 2016-12-05. + */ +@Data +class PetRequest { + private String id; + + @JsonFormat(pattern = "yyyy-MM-dd") + private Date birthDate; + + @Size(min = 1) + private String name; + + private String type; +} diff --git a/spring-petclinic-microservices/spring-petclinic-customers-service/src/main/java/org/springframework/samples/petclinic/customers/web/PetResource.java b/spring-petclinic-microservices/spring-petclinic-customers-service/src/main/java/org/springframework/samples/petclinic/customers/web/PetResource.java new file mode 100644 index 000000000..c47715b7b --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-customers-service/src/main/java/org/springframework/samples/petclinic/customers/web/PetResource.java @@ -0,0 +1,102 @@ +/* + * Copyright 2002-2017 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.samples.petclinic.customers.web; + +import io.micrometer.core.annotation.Timed; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpStatus; +import org.springframework.samples.petclinic.customers.model.Owner; +import org.springframework.samples.petclinic.customers.model.OwnerRepository; +import org.springframework.samples.petclinic.customers.model.Pet; +import org.springframework.samples.petclinic.customers.model.PetRepository; +import org.springframework.web.bind.annotation.*; +import reactor.core.publisher.Mono; + +import java.util.List; +import java.util.Optional; + +/** + * @author Juergen Hoeller + * @author Ken Krebs + * @author Arjen Poutsma + * @author Maciej Szarlinski + */ +@RestController +@Timed("petclinic.pet") +@RequiredArgsConstructor +@Slf4j +class PetResource { + + private final PetRepository petRepository; + private final OwnerRepository ownerRepository; + + @GetMapping("/petTypes") + public List getPetTypes() { + List petTypes = petRepository.getPetTypes(); + return petTypes; + } + + @PostMapping("/owners/{ownerId}/pets") + @ResponseStatus(HttpStatus.CREATED) + public Pet processCreationForm( + @RequestBody PetRequest petRequest, + @PathVariable("ownerId") String ownerId) { + + final Pet pet = new Pet(); + Optional optionalOwner = ownerRepository.findById(ownerId); + + optionalOwner.ifPresent(owner -> { + owner.addPet(pet); + }); + return save(pet, petRequest); + } + + @PutMapping("/owners/*/pets/{petId}") + @ResponseStatus(HttpStatus.NO_CONTENT) + public void processUpdateForm(@RequestBody PetRequest petRequest) { + String petId = petRequest.getId(); + Optional petOptional = findPetById(petId); + petOptional.ifPresent(pet -> { + save(pet, petRequest); + }); + } + + private Pet save(final Pet pet, final PetRequest petRequest) { + + pet.setName(petRequest.getName()); + pet.setBirthDate(petRequest.getBirthDate()); + pet.setType(petRequest.getType()); + + log.info("Saving pet {}", pet); + return petRepository.save(pet); + } + + @GetMapping("owners/*/pets/{petId}") + public PetDetails findPet(@PathVariable("petId") String petId) { + return new PetDetails(findPetById(petId)); + } + + private Optional findPetById(String petId) { + Optional optionalPet = petRepository.findById(petId); + + if (!optionalPet.isPresent()) { + throw new ResourceNotFoundException("Pet " + petId + " not found"); + } + return optionalPet; + } + +} diff --git a/spring-petclinic-microservices/spring-petclinic-customers-service/src/main/java/org/springframework/samples/petclinic/customers/web/ResourceNotFoundException.java b/spring-petclinic-microservices/spring-petclinic-customers-service/src/main/java/org/springframework/samples/petclinic/customers/web/ResourceNotFoundException.java new file mode 100644 index 000000000..c321a99d0 --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-customers-service/src/main/java/org/springframework/samples/petclinic/customers/web/ResourceNotFoundException.java @@ -0,0 +1,13 @@ +package org.springframework.samples.petclinic.customers.web; + +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ResponseStatus; + +@ResponseStatus(value = HttpStatus.NOT_FOUND) +public class ResourceNotFoundException extends RuntimeException { + + public ResourceNotFoundException(String message) { + super(message); + } + +} diff --git a/spring-petclinic-microservices/spring-petclinic-customers-service/src/main/resources/application.properties b/spring-petclinic-microservices/spring-petclinic-customers-service/src/main/resources/application.properties new file mode 100644 index 000000000..4b4cf6d5b --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-customers-service/src/main/resources/application.properties @@ -0,0 +1,35 @@ +azure.keyvault.enabled=true +azure.keyvault.uri=${AZURE_KEYVAULT_URI} +azure.keyvault.client-id=${AZURE_KEYVAULT_CLIENTID} +azure.keyvault.client-key=${AZURE_KEYVAULT_CLIENTKEY} +azure.keyvault.tenant-id=${AZURE_KEYVAULT_TENANTID} +azure.keyvault.token-acquire-timeout-seconds=60 +azure.keyvault.refresh-interval=1800000 +azure.keyvault.secret-keys=cosmosdburi,cosmosdbkey,cosmosdbsecondarykey, redisuri, redispassword + +# Azure cosmos db properties +azure.cosmosdb.uri=${cosmosdburi} +azure.cosmosdb.key=${cosmosdbkey} +azure.cosmosdb.secondaryKey=${cosmosdbsecondarykey} +azure.cosmosdb.database=end2endsample +# Populate query metrics +cosmos.queryMetricsEnabled=true + +# Azure redis cache properties +spring.cache.type=redis +spring.redis.host=${redisuri} +spring.redis.password=${redispassword} +spring.redis.port=6380 +spring.redis.ssl=true + +# For AAD Sample code +# Azure AAD properties +## Specifies your Active Directory ID: +#azure.activedirectory.tenant-id=22222222-2222-2222-2222-222222222222 +## Specifies your App Registration's Application ID: +#azure.activedirectory.client-id=11111111-1111-1111-1111-1111111111111111 +## Specifies your App Registration's secret key: +#azure.activedirectory.client-secret=AbCdEfGhIjKlMnOpQrStUvWxYz== +## Specifies the list of Active Directory groups to use for authorization: +#azure.activedirectory.user-group.allowed-groups=group1 + diff --git a/spring-petclinic-microservices/spring-petclinic-customers-service/src/main/resources/bootstrap.yml b/spring-petclinic-microservices/spring-petclinic-customers-service/src/main/resources/bootstrap.yml new file mode 100644 index 000000000..476bc5578 --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-customers-service/src/main/resources/bootstrap.yml @@ -0,0 +1,29 @@ +eureka: + client: + register-with-eureka: true + fetch-registry: true +spring: + cloud: + config: + uri: http://localhost:8888 + application: + name: customers-service +--- +spring: + profiles: docker + cloud: + config: + uri: http://config-server:8888 + +--- +spring: + profiles: local + cloud: + config: + uri: http://localhost:8888 +server: + port: 8081 +eureka: + client: + serviceUrl: + defaultZone: http://localhost:8761/eureka/ diff --git a/spring-petclinic-microservices/spring-petclinic-customers-service/src/main/resources/logback-spring.xml b/spring-petclinic-microservices/spring-petclinic-customers-service/src/main/resources/logback-spring.xml new file mode 100644 index 000000000..5d03f7941 --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-customers-service/src/main/resources/logback-spring.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/spring-petclinic-microservices/spring-petclinic-customers-service/src/test/java/org/springframework/samples/petclinic/customers/web/PetResourceTest.java b/spring-petclinic-microservices/spring-petclinic-customers-service/src/test/java/org/springframework/samples/petclinic/customers/web/PetResourceTest.java new file mode 100644 index 000000000..5303a4198 --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-customers-service/src/test/java/org/springframework/samples/petclinic/customers/web/PetResourceTest.java @@ -0,0 +1,78 @@ +package org.springframework.samples.petclinic.customers.web; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.http.MediaType; +import org.springframework.samples.petclinic.customers.model.Owner; +import org.springframework.samples.petclinic.customers.model.OwnerRepository; +import org.springframework.samples.petclinic.customers.model.Pet; +import org.springframework.samples.petclinic.customers.model.PetRepository; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.web.servlet.MockMvc; +import reactor.core.publisher.Mono; + +import java.util.Optional; + +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.when; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +/** + * @author Maciej Szarlinski + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ExtendWith(SpringExtension.class) +@AutoConfigureMockMvc +@SpringBootTest +@ActiveProfiles("test") +class PetResourceTest { + + @Autowired + MockMvc mvc; + + @MockBean + PetRepository petRepository; + + @MockBean + OwnerRepository ownerRepository; + + @Test + void shouldGetAPetInJSonFormat() throws Exception { + + Pet pet = setupPet(); + + given(petRepository.findById("2")).willReturn(Optional.of(pet)); + + + mvc.perform(get("/owners/2/pets/2").accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(content().contentType("application/json")) + .andExpect(jsonPath("$.id").value(2)) + .andExpect(jsonPath("$.name").value("Basil")); + } + + private Pet setupPet() { + Owner owner = new Owner(); + owner.setFirstName("George"); + owner.setLastName("Bush"); + + Pet pet = new Pet(); + + pet.setName("Basil"); + + pet.setId("2"); + + pet.setType("dog"); + + owner.addPet(pet); + return pet; + } +} diff --git a/spring-petclinic-microservices/spring-petclinic-customers-service/src/test/resources/application-test.yml b/spring-petclinic-microservices/spring-petclinic-customers-service/src/test/resources/application-test.yml new file mode 100644 index 000000000..07e39e627 --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-customers-service/src/test/resources/application-test.yml @@ -0,0 +1,8 @@ + +spring: + datasource: + schema: classpath*:db/hsqldb/schema.sql + data: classpath*:db/hsqldb/data.sql + +logging.level.org.springframework: INFO + diff --git a/spring-petclinic-microservices/spring-petclinic-customers-service/src/test/resources/bootstrap-test.yml b/spring-petclinic-microservices/spring-petclinic-customers-service/src/test/resources/bootstrap-test.yml new file mode 100644 index 000000000..cb8e4f5f7 --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-customers-service/src/test/resources/bootstrap-test.yml @@ -0,0 +1,2 @@ +spring.cloud.config.enabled: false +eureka.client.enabled: false diff --git a/spring-petclinic-microservices/spring-petclinic-discovery-server/pom.xml b/spring-petclinic-microservices/spring-petclinic-discovery-server/pom.xml new file mode 100644 index 000000000..d6416ef3f --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-discovery-server/pom.xml @@ -0,0 +1,73 @@ + + + 4.0.0 + + org.springframework.samples.petclinic.discovery + spring-petclinic-discovery-server + jar + Spring PetClinic Discovery Server + + + org.springframework.samples + spring-petclinic-microservices + 2.3.6 + + + + 8761 + ${basedir}/../docker + + + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + org.springframework.cloud + spring-cloud-starter-config + + + org.springframework.cloud + spring-cloud-starter-netflix-eureka-server + + + + + org.junit.jupiter + junit-jupiter-api + test + + + org.junit.jupiter + junit-jupiter-engine + test + + + + + org.glassfish.jaxb + jaxb-runtime + + + + + + buildDocker + + + + com.spotify + docker-maven-plugin + ${docker.plugin.version} + + + + + + diff --git a/spring-petclinic-microservices/spring-petclinic-discovery-server/src/main/java/org/springframework/samples/petclinic/discovery/DiscoveryServerApplication.java b/spring-petclinic-microservices/spring-petclinic-discovery-server/src/main/java/org/springframework/samples/petclinic/discovery/DiscoveryServerApplication.java new file mode 100644 index 000000000..59e2c3396 --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-discovery-server/src/main/java/org/springframework/samples/petclinic/discovery/DiscoveryServerApplication.java @@ -0,0 +1,32 @@ +/* + * Copyright 2002-2017 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.samples.petclinic.discovery; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer; + +/** + * @author Maciej Szarlinski + */ +@EnableEurekaServer +@SpringBootApplication +public class DiscoveryServerApplication { + + public static void main(String[] args) { + SpringApplication.run(DiscoveryServerApplication.class, args); + } +} diff --git a/spring-petclinic-microservices/spring-petclinic-discovery-server/src/main/resources/bootstrap.yml b/spring-petclinic-microservices/spring-petclinic-discovery-server/src/main/resources/bootstrap.yml new file mode 100644 index 000000000..a0a0b0d4a --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-discovery-server/src/main/resources/bootstrap.yml @@ -0,0 +1,21 @@ +spring: + cloud: + config: + uri: http://localhost:8888 + application: + name: discovery-server +--- +spring: + profiles: docker + cloud: + config: + uri: http://config-server:8888 + +--- +spring: + profiles: local + cloud: + config: + uri: http://localhost:8888 +server: + port: 8761 diff --git a/spring-petclinic-microservices/spring-petclinic-discovery-server/src/test/java/org/springframework/samples/petclinic/discovery/DiscoveryServerApplicationTests.java b/spring-petclinic-microservices/spring-petclinic-discovery-server/src/test/java/org/springframework/samples/petclinic/discovery/DiscoveryServerApplicationTests.java new file mode 100644 index 000000000..a1ede35e1 --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-discovery-server/src/test/java/org/springframework/samples/petclinic/discovery/DiscoveryServerApplicationTests.java @@ -0,0 +1,28 @@ +/* + * Copyright 2002-2017 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.samples.petclinic.discovery; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class DiscoveryServerApplicationTests { + + @Test + void contextLoads() { + } + +} diff --git a/spring-petclinic-microservices/spring-petclinic-vets-service/.factorypath b/spring-petclinic-microservices/spring-petclinic-vets-service/.factorypath new file mode 100644 index 000000000..ccb3d4aae --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-vets-service/.factorypath @@ -0,0 +1,166 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/spring-petclinic-microservices/spring-petclinic-vets-service/.gitignore b/spring-petclinic-microservices/spring-petclinic-vets-service/.gitignore new file mode 100644 index 000000000..c71ea97ab --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-vets-service/.gitignore @@ -0,0 +1 @@ +/.apt_generated/ diff --git a/spring-petclinic-microservices/spring-petclinic-vets-service/pom.xml b/spring-petclinic-microservices/spring-petclinic-vets-service/pom.xml new file mode 100644 index 000000000..61edd128f --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-vets-service/pom.xml @@ -0,0 +1,151 @@ + + + 4.0.0 + + org.springframework.samples.petclinic.vets + spring-petclinic-vets-service + jar + Spring PetClinic Vets Service + + + org.springframework.samples + spring-petclinic-microservices + 2.3.6 + + + + 8081 + ${basedir}/../docker + + + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-configuration-processor + true + + + org.springframework.boot + spring-boot-starter-actuator + + + org.springframework.boot + spring-boot-starter-cache + + + org.springframework.boot + spring-boot-starter-test + test + + + + + org.springframework.cloud + spring-cloud-sleuth-zipkin + + + org.springframework.cloud + spring-cloud-starter-config + + + org.springframework.cloud + spring-cloud-starter-netflix-eureka-client + + + org.springframework.cloud + spring-cloud-starter-sleuth + + + + + org.projectlombok + lombok + + + javax.cache + cache-api + + + org.ehcache + ehcache + + + org.jolokia + jolokia-core + + + org.hsqldb + hsqldb + runtime + + + + com.azure + azure-spring-data-cosmos + 3.2.0 + + + com.azure.spring + azure-spring-boot-starter-keyvault-secrets + 3.1.0 + + + org.springframework.data + spring-data-rest-core + + + io.micrometer + micrometer-registry-prometheus + + + javax.validation + validation-api + + + + + org.junit.jupiter + junit-jupiter-api + test + + + org.junit.jupiter + junit-jupiter-engine + test + + + jakarta.persistence + jakarta.persistence-api + + + junit + junit + test + + + + + + buildDocker + + + + com.spotify + docker-maven-plugin + ${docker.plugin.version} + + + + + + diff --git a/spring-petclinic-microservices/spring-petclinic-vets-service/src/main/java/org/springframework/samples/petclinic/vets/PopulateSeedData.java b/spring-petclinic-microservices/spring-petclinic-vets-service/src/main/java/org/springframework/samples/petclinic/vets/PopulateSeedData.java new file mode 100644 index 000000000..414796dde --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-vets-service/src/main/java/org/springframework/samples/petclinic/vets/PopulateSeedData.java @@ -0,0 +1,50 @@ +package org.springframework.samples.petclinic.vets; + +import com.azure.cosmos.implementation.guava25.collect.Lists; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.samples.petclinic.vets.model.Specialty; +import org.springframework.samples.petclinic.vets.model.Vet; +import org.springframework.samples.petclinic.vets.model.VetRepository; +import org.springframework.stereotype.Component; +import reactor.core.publisher.Flux; + +import javax.annotation.PostConstruct; +import java.text.ParseException; +import java.util.HashSet; + + +@Component +public class PopulateSeedData { + + private static final Logger logger = LoggerFactory.getLogger(PopulateSeedData.class); + + @Autowired + private VetRepository repository; + + @PostConstruct + public void populateSeedData() throws ParseException { + final Specialty speciality1 = new Specialty(1, "radiology"); + final Specialty speciality2 = new Specialty(2, "surgery"); + final Specialty speciality3 = new Specialty(3, "dentistry"); + + final Vet vet1 = new Vet(1, "James", "Carter", null); + final Vet vet2 = new Vet(2, "Helen", "Leary", new HashSet() {{ + add(speciality1); + }}); + final Vet vet3 = new Vet(3, "Linda", "Douglas", new HashSet() {{ + add(speciality2); + add(speciality3); + }}); + final Vet vet4 = new Vet(4, "Rafael", "Ortega", new HashSet() {{ + add(speciality2); + }}); + final Vet vet5 = new Vet(5, "Henry", "Stevens", new HashSet() {{ + add(speciality1); + }}); + final Vet vet6 = new Vet(6, "James", "Carter", null); + + this.repository.saveAll(Lists.newArrayList(vet1, vet2, vet3, vet4, vet5, vet6)); + } +} diff --git a/spring-petclinic-microservices/spring-petclinic-vets-service/src/main/java/org/springframework/samples/petclinic/vets/VetsServiceApplication.java b/spring-petclinic-microservices/spring-petclinic-vets-service/src/main/java/org/springframework/samples/petclinic/vets/VetsServiceApplication.java new file mode 100644 index 000000000..de969f4eb --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-vets-service/src/main/java/org/springframework/samples/petclinic/vets/VetsServiceApplication.java @@ -0,0 +1,37 @@ +/* + * Copyright 2002-2017 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.samples.petclinic.vets; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.cloud.client.discovery.EnableDiscoveryClient; +import org.springframework.samples.petclinic.vets.model.VetRepository; +import org.springframework.samples.petclinic.vets.system.VetsProperties; + +/** + * @author Maciej Szarlinski + */ +@EnableDiscoveryClient +@SpringBootApplication +@EnableConfigurationProperties(VetsProperties.class) +public class VetsServiceApplication { + + public static void main(String[] args) { + SpringApplication.run(VetsServiceApplication.class, args); + } +} diff --git a/spring-petclinic-microservices/spring-petclinic-vets-service/src/main/java/org/springframework/samples/petclinic/vets/model/CosmosProperties.java b/spring-petclinic-microservices/spring-petclinic-vets-service/src/main/java/org/springframework/samples/petclinic/vets/model/CosmosProperties.java new file mode 100644 index 000000000..cfa48186f --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-vets-service/src/main/java/org/springframework/samples/petclinic/vets/model/CosmosProperties.java @@ -0,0 +1,22 @@ +package org.springframework.samples.petclinic.vets.model; + +import lombok.Getter; +import lombok.Setter; +import org.springframework.boot.context.properties.ConfigurationProperties; + + +@Getter +@Setter +@ConfigurationProperties(prefix = "azure.cosmosdb") +class CosmosProperties { + + private String uri; + + private String key; + + private String secondaryKey; + + private String database; + + private boolean populateQueryMetrics; +} diff --git a/spring-petclinic-microservices/spring-petclinic-vets-service/src/main/java/org/springframework/samples/petclinic/vets/model/Specialty.java b/spring-petclinic-microservices/spring-petclinic-vets-service/src/main/java/org/springframework/samples/petclinic/vets/model/Specialty.java new file mode 100644 index 000000000..05fa560da --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-vets-service/src/main/java/org/springframework/samples/petclinic/vets/model/Specialty.java @@ -0,0 +1,51 @@ +/* + * Copyright 2002-2017 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.samples.petclinic.vets.model; + +import lombok.AllArgsConstructor; +import lombok.NoArgsConstructor; + +import javax.persistence.Id; + +/** + * Models a {@link Vet Vet's} specialty (for example, dentistry). + * + * @author Juergen Hoeller + */ +@AllArgsConstructor +@NoArgsConstructor +public class Specialty { + @Id + private Integer id; + + private String name; + + public Integer getId() { + return id; + } + + public void setId(final Integer id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(final String name) { + this.name = name; + } +} diff --git a/spring-petclinic-microservices/spring-petclinic-vets-service/src/main/java/org/springframework/samples/petclinic/vets/model/Vet.java b/spring-petclinic-microservices/spring-petclinic-vets-service/src/main/java/org/springframework/samples/petclinic/vets/model/Vet.java new file mode 100644 index 000000000..d731db9e7 --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-vets-service/src/main/java/org/springframework/samples/petclinic/vets/model/Vet.java @@ -0,0 +1,110 @@ +/* + * Copyright 2002-2017 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.samples.petclinic.vets.model; + +import com.azure.spring.data.cosmos.core.mapping.Container; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.NoArgsConstructor; +import org.springframework.beans.support.MutableSortDefinition; +import org.springframework.beans.support.PropertyComparator; + +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.validation.constraints.NotEmpty; +import javax.xml.bind.annotation.XmlElement; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * Simple JavaBean domain object representing a veterinarian. + * + * @author Ken Krebs + * @author Juergen Hoeller + * @author Sam Brannen + * @author Arjen Poutsma + * @author Maciej Szarlinski + */ + +@Container(containerName = "vets") +@Builder(builderMethodName = "vet") +@AllArgsConstructor +@NoArgsConstructor +public class Vet { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Integer id; + + @NotEmpty + private String firstName; + + @NotEmpty + private String lastName; + + private Set specialties; + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getFirstName() { + return this.firstName; + } + + public void setFirstName(String firstName) { + this.firstName = firstName; + } + + public String getLastName() { + return this.lastName; + } + + public void setLastName(String lastName) { + this.lastName = lastName; + } + + protected Set getSpecialtiesInternal() { + if (this.specialties == null) { + this.specialties = new HashSet<>(); + } + return this.specialties; + } + + @XmlElement + public List getSpecialties() { + List sortedSpecs = new ArrayList<>(getSpecialtiesInternal()); + PropertyComparator.sort(sortedSpecs, new MutableSortDefinition("name", true, true)); + return Collections.unmodifiableList(sortedSpecs); + } + + public int getNrOfSpecialties() { + return getSpecialtiesInternal().size(); + } + + public void addSpecialty(Specialty specialty) { + getSpecialtiesInternal().add(specialty); + } + +} diff --git a/spring-petclinic-microservices/spring-petclinic-vets-service/src/main/java/org/springframework/samples/petclinic/vets/model/VetRepository.java b/spring-petclinic-microservices/spring-petclinic-vets-service/src/main/java/org/springframework/samples/petclinic/vets/model/VetRepository.java new file mode 100644 index 000000000..2b77e1dc1 --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-vets-service/src/main/java/org/springframework/samples/petclinic/vets/model/VetRepository.java @@ -0,0 +1,33 @@ +/* + * Copyright 2002-2017 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.samples.petclinic.vets.model; + +import com.azure.spring.data.cosmos.repository.CosmosRepository; +import org.springframework.stereotype.Repository; + +/** + * Repository class for Vet domain objects All method names are compliant with Spring Data naming + * conventions so this interface can easily be extended for Spring Data See here: http://static.springsource.org/spring-data/jpa/docs/current/reference/html/jpa.repositories.html#jpa.query-methods.query-creation + * + * @author Ken Krebs + * @author Juergen Hoeller + * @author Sam Brannen + * @author Michael Isvy + * @author Maciej Szarlinski + */ +@Repository +public interface VetRepository extends CosmosRepository { +} diff --git a/spring-petclinic-microservices/spring-petclinic-vets-service/src/main/java/org/springframework/samples/petclinic/vets/model/VetsAppConfiguration.java b/spring-petclinic-microservices/spring-petclinic-vets-service/src/main/java/org/springframework/samples/petclinic/vets/model/VetsAppConfiguration.java new file mode 100644 index 000000000..05a8c4c4a --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-vets-service/src/main/java/org/springframework/samples/petclinic/vets/model/VetsAppConfiguration.java @@ -0,0 +1,76 @@ +package org.springframework.samples.petclinic.vets.model; + + +import com.azure.core.credential.AzureKeyCredential; +import com.azure.cosmos.CosmosClientBuilder; +import com.azure.cosmos.DirectConnectionConfig; +import com.azure.cosmos.GatewayConnectionConfig; +import com.azure.spring.data.cosmos.config.AbstractCosmosConfiguration; +import com.azure.spring.data.cosmos.config.CosmosConfig; +import com.azure.spring.data.cosmos.core.ResponseDiagnostics; +import com.azure.spring.data.cosmos.core.ResponseDiagnosticsProcessor; +import com.azure.spring.data.cosmos.repository.config.EnableCosmosRepositories; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.PropertySource; +import org.springframework.lang.Nullable; + + +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + + +@Configuration +@EnableConfigurationProperties(CosmosProperties.class) +@EnableCosmosRepositories +@PropertySource("classpath:application.properties") +public class VetsAppConfiguration extends AbstractCosmosConfiguration { + + private static final Logger logger = LoggerFactory.getLogger(VetsAppConfiguration.class); + + @Autowired + private CosmosProperties properties; + + private AzureKeyCredential azureKeyCredential; + + @Bean + public CosmosClientBuilder cosmosClientBuilder() { + this.azureKeyCredential = new AzureKeyCredential(properties.getKey()); + DirectConnectionConfig directConnectionConfig = new DirectConnectionConfig(); + GatewayConnectionConfig gatewayConnectionConfig = new GatewayConnectionConfig(); + return new CosmosClientBuilder() + .endpoint(properties.getUri()) + .credential(azureKeyCredential) + .directMode(directConnectionConfig, gatewayConnectionConfig); + } + + @Override + public CosmosConfig cosmosConfig() { + return CosmosConfig.builder() + .enableQueryMetrics(properties.isPopulateQueryMetrics()) + .responseDiagnosticsProcessor(new ResponseDiagnosticsProcessorImplementation()) + .build(); + } + + + public void switchToSecondaryKey() { + this.azureKeyCredential.update(properties.getSecondaryKey()); + } + + @Override + protected String getDatabaseName() { + return properties.getDatabase(); + } + + private static class ResponseDiagnosticsProcessorImplementation implements ResponseDiagnosticsProcessor { + + @Override + public void processResponseDiagnostics(@Nullable ResponseDiagnostics responseDiagnostics) { + logger.info("Response Diagnostics {}", responseDiagnostics); + } + } +} diff --git a/spring-petclinic-microservices/spring-petclinic-vets-service/src/main/java/org/springframework/samples/petclinic/vets/system/CacheConfig.java b/spring-petclinic-microservices/spring-petclinic-vets-service/src/main/java/org/springframework/samples/petclinic/vets/system/CacheConfig.java new file mode 100644 index 000000000..6941a1446 --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-vets-service/src/main/java/org/springframework/samples/petclinic/vets/system/CacheConfig.java @@ -0,0 +1,30 @@ +/* + * Copyright 2002-2017 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.samples.petclinic.vets.system; + +import org.springframework.cache.annotation.EnableCaching; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Profile; + +/** + * Cache could be disable in unit test. + * @author Maciej Szarlinski + */ +@Configuration +@EnableCaching +@Profile("production") +class CacheConfig { +} diff --git a/spring-petclinic-microservices/spring-petclinic-vets-service/src/main/java/org/springframework/samples/petclinic/vets/system/VetsProperties.java b/spring-petclinic-microservices/spring-petclinic-vets-service/src/main/java/org/springframework/samples/petclinic/vets/system/VetsProperties.java new file mode 100644 index 000000000..e6529f102 --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-vets-service/src/main/java/org/springframework/samples/petclinic/vets/system/VetsProperties.java @@ -0,0 +1,40 @@ +/* + * Copyright 2002-2017 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.samples.petclinic.vets.system; + +import lombok.Data; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * Typesafe custom configuration. + * + * @author Maciej Szarlinski + */ +@Data +@ConfigurationProperties(prefix = "vets") +public class VetsProperties { + + private Cache cache; + + @Data + public static class Cache { + + private int ttl; + + private int heapSize; + } +} diff --git a/spring-petclinic-microservices/spring-petclinic-vets-service/src/main/java/org/springframework/samples/petclinic/vets/web/VetResource.java b/spring-petclinic-microservices/spring-petclinic-vets-service/src/main/java/org/springframework/samples/petclinic/vets/web/VetResource.java new file mode 100644 index 000000000..d6d19611b --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-vets-service/src/main/java/org/springframework/samples/petclinic/vets/web/VetResource.java @@ -0,0 +1,55 @@ +/* + * Copyright 2002-2017 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.samples.petclinic.vets.web; + +import io.micrometer.core.annotation.Timed; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.samples.petclinic.vets.model.Vet; +import org.springframework.samples.petclinic.vets.model.VetRepository; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author Juergen Hoeller + * @author Mark Fisher + * @author Ken Krebs + * @author Arjen Poutsma + * @author Maciej Szarlinski + */ +@RequestMapping("/vets") +@RestController +@RequiredArgsConstructor +@Timed("petclinic.vet") +@Slf4j +class +VetResource { + + + private final VetRepository vetRepository; + + @GetMapping + public List showResourcesVetList() { + List list = new ArrayList<>(); + vetRepository.findAll().forEach(list::add); + return list; + } +} diff --git a/spring-petclinic-microservices/spring-petclinic-vets-service/src/main/resources/application.properties b/spring-petclinic-microservices/spring-petclinic-vets-service/src/main/resources/application.properties new file mode 100644 index 000000000..2b931bd0c --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-vets-service/src/main/resources/application.properties @@ -0,0 +1,30 @@ +spring.cache.cache-names=vets + +azure.keyvault.enabled=true +azure.keyvault.uri=${AZURE_KEYVAULT_URI} +azure.keyvault.client-id=${AZURE_KEYVAULT_CLIENTID} +azure.keyvault.client-key=${AZURE_KEYVAULT_CLIENTKEY} +azure.keyvault.tenant-id=${AZURE_KEYVAULT_TENANTID} + +azure.keyvault.token-acquire-timeout-seconds=60 +azure.keyvault.refresh-interval=1800000 +azure.keyvault.secret-keys=cosmosdburi,cosmosdbkey,cosmosdbsecondarykey, redisuri, redispassword + +# Azure cosmos db properties +azure.cosmosdb.uri=${cosmosdburi} +azure.cosmosdb.key=${cosmosdbkey} +azure.cosmosdb.secondaryKey=${cosmosdbsecondarykey} +azure.cosmosdb.database=end2endsample +# Populate query metrics +cosmos.queryMetricsEnabled=true + + +# Properties for Setting Azure active directory +## Specifies your Active Directory ID: +#azure.activedirectory.tenant-id=22222222-2222-2222-2222-222222222222 +## Specifies your App Registration's Application ID: +#azure.activedirectory.client-id=11111111-1111-1111-1111-1111111111111111 +## Specifies your App Registration's secret key: +#azure.activedirectory.client-secret=AbCdEfGhIjKlMnOpQrStUvWxYz== +## Specifies the list of Active Directory groups to use for authorization: +#azure.activedirectory.user-group.allowed-groups=group1 diff --git a/spring-petclinic-microservices/spring-petclinic-vets-service/src/main/resources/bootstrap.yml b/spring-petclinic-microservices/spring-petclinic-vets-service/src/main/resources/bootstrap.yml new file mode 100644 index 000000000..477d86f29 --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-vets-service/src/main/resources/bootstrap.yml @@ -0,0 +1,30 @@ +eureka: + client: + register-with-eureka: true + fetch-registry: true +spring: + cloud: + config: + uri: http://localhost:8888 + application: + name: vets-service +--- +spring: + profiles: docker + cloud: + config: + uri: http://config-server:8888 +--- +spring: + profiles: local + cloud: + config: + uri: http://localhost:8888 + zipkin: + baseUrl: http://localhost:9411 +eureka: + client: + serviceUrl: + defaultZone: http://localhost:8761/eureka/ +server: + port: 8083 diff --git a/spring-petclinic-microservices/spring-petclinic-vets-service/src/main/resources/logback-spring.xml b/spring-petclinic-microservices/spring-petclinic-vets-service/src/main/resources/logback-spring.xml new file mode 100644 index 000000000..5d03f7941 --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-vets-service/src/main/resources/logback-spring.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/spring-petclinic-microservices/spring-petclinic-vets-service/src/test/java/org/springframework/samples/petclinic/vets/web/VetResourceTest.java b/spring-petclinic-microservices/spring-petclinic-vets-service/src/test/java/org/springframework/samples/petclinic/vets/web/VetResourceTest.java new file mode 100644 index 000000000..81d2da8c3 --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-vets-service/src/test/java/org/springframework/samples/petclinic/vets/web/VetResourceTest.java @@ -0,0 +1,73 @@ +/* + * Copyright 2002-2017 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.samples.petclinic.vets.web; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.http.MediaType; +import org.springframework.samples.petclinic.vets.model.Vet; +import org.springframework.samples.petclinic.vets.model.VetRepository; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.MvcResult; +import reactor.core.publisher.Flux; + +import java.util.Arrays; +import java.util.List; + +import static java.util.Arrays.asList; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.when; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.asyncDispatch; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +/** + * @author Maciej Szarlinski + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ExtendWith(SpringExtension.class) +@AutoConfigureMockMvc +@SpringBootTest +@ActiveProfiles("test") +class VetResourceTest { + + @Autowired + MockMvc mvc; + + @MockBean + VetRepository vetRepository; + + @Test + void shouldGetAListOfVets() throws Exception { + List vet = Arrays.asList(Vet.vet().id(1).build(), Vet.vet().id(2).build() + ); + + given(vetRepository.findAll()).willReturn(vet); + + mvc.perform(get("/vets").accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$[0].id").value(1)); + } +} diff --git a/spring-petclinic-microservices/spring-petclinic-vets-service/src/test/resources/application-test.yml b/spring-petclinic-microservices/spring-petclinic-vets-service/src/test/resources/application-test.yml new file mode 100644 index 000000000..a00a1d789 --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-vets-service/src/test/resources/application-test.yml @@ -0,0 +1,10 @@ + +spring: + datasource: + schema: classpath*:db/hsqldb/schema.sql + data: classpath*:db/hsqldb/data.sql + +vets: + cache: + ttl: 10 + heap-size: 10 diff --git a/spring-petclinic-microservices/spring-petclinic-vets-service/src/test/resources/bootstrap-test.yml b/spring-petclinic-microservices/spring-petclinic-vets-service/src/test/resources/bootstrap-test.yml new file mode 100644 index 000000000..cb8e4f5f7 --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-vets-service/src/test/resources/bootstrap-test.yml @@ -0,0 +1,2 @@ +spring.cloud.config.enabled: false +eureka.client.enabled: false diff --git a/spring-petclinic-microservices/spring-petclinic-visits-service/pom.xml b/spring-petclinic-microservices/spring-petclinic-visits-service/pom.xml new file mode 100644 index 000000000..5b2b8328b --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-visits-service/pom.xml @@ -0,0 +1,137 @@ + + + 4.0.0 + + org.springframework.samples.petclinic.visits + spring-petclinic-visits-service + jar + Spring PetClinic Visits Service + + + org.springframework.samples + spring-petclinic-microservices + 2.3.6 + + + + 8081 + ${basedir}/../docker + + + + + + org.springframework.boot + spring-boot-starter-actuator + + + com.azure + azure-spring-data-cosmos + 3.2.0 + + + org.springframework.data + spring-data-rest-webmvc + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-test + test + + + + + org.springframework.cloud + spring-cloud-starter-config + + + org.springframework.cloud + spring-cloud-starter-netflix-eureka-client + + + org.springframework.cloud + spring-cloud-starter-sleuth + + + org.springframework.cloud + spring-cloud-sleuth-zipkin + + + + + org.projectlombok + lombok + + + org.hsqldb + hsqldb + runtime + + + org.jolokia + jolokia-core + + + + io.micrometer + micrometer-registry-prometheus + + + javax.validation + validation-api + + + + + + + org.junit.jupiter + junit-jupiter-api + test + + + org.junit.jupiter + junit-jupiter-engine + test + + + jakarta.persistence + jakarta.persistence-api + + + junit + junit + test + + + com.azure + azure-spring-data-cosmos + 3.2.0 + + + com.azure.spring + azure-spring-boot-starter-keyvault-secrets + 3.1.0 + + + + + + buildDocker + + + + com.spotify + docker-maven-plugin + ${docker.plugin.version} + + + + + + diff --git a/spring-petclinic-microservices/spring-petclinic-visits-service/src/main/java/org/springframework/samples/petclinic/visits/PopulateSeedData.java b/spring-petclinic-microservices/spring-petclinic-visits-service/src/main/java/org/springframework/samples/petclinic/visits/PopulateSeedData.java new file mode 100644 index 000000000..faea99b3c --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-visits-service/src/main/java/org/springframework/samples/petclinic/visits/PopulateSeedData.java @@ -0,0 +1,40 @@ +package org.springframework.samples.petclinic.visits; + +import com.azure.cosmos.implementation.guava25.collect.Lists; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.samples.petclinic.visits.model.Visit; +import org.springframework.samples.petclinic.visits.model.VisitRepository; +import org.springframework.stereotype.Component; +import reactor.core.publisher.Flux; + +import javax.annotation.PostConstruct; +import java.text.ParseException; +import java.text.SimpleDateFormat; + + +@Component +public class PopulateSeedData { + + private static final Logger logger = LoggerFactory.getLogger(PopulateSeedData.class); + + @Autowired + private VisitRepository visitRepository; + + // @PostConstruct +// @Order(2) // not evaluated by Spring +// void postConstruct(){ +// logger.info("@PostConstruct"); +// } + @PostConstruct + // @Order(2) // not evaluated by Spring + public void populateSeedData() throws ParseException { + final Visit visit1 = new Visit("1", "7", new SimpleDateFormat("yyyy-dd-MM").parse("2010-03-04"), "rabies shot"); + final Visit visit2 = new Visit("2", "8", new SimpleDateFormat("yyyy-dd-MM").parse("2011-03-04"), "rabies shot"); + final Visit visit3 = new Visit("3", "8", new SimpleDateFormat("yyyy-dd-MM").parse("2009-06-04"), "neutered"); + final Visit visit4 = new Visit("4", "7", new SimpleDateFormat("yyyy-dd-MM").parse("2008-09-04"), "spayed"); + + this.visitRepository.saveAll(Lists.newArrayList(visit1, visit2, visit3, visit4)); + } +} diff --git a/spring-petclinic-microservices/spring-petclinic-visits-service/src/main/java/org/springframework/samples/petclinic/visits/VisitsServiceApplication.java b/spring-petclinic-microservices/spring-petclinic-visits-service/src/main/java/org/springframework/samples/petclinic/visits/VisitsServiceApplication.java new file mode 100644 index 000000000..f5f8d3f82 --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-visits-service/src/main/java/org/springframework/samples/petclinic/visits/VisitsServiceApplication.java @@ -0,0 +1,35 @@ +/* + * Copyright 2002-2017 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.samples.petclinic.visits; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.cloud.client.discovery.EnableDiscoveryClient; +import org.springframework.samples.petclinic.visits.model.VisitRepository; + +/** + * @author Maciej Szarlinski + */ +@EnableDiscoveryClient +@SpringBootApplication +public class VisitsServiceApplication { + + public static void main(String[] args) { + SpringApplication.run(VisitsServiceApplication.class, args); + } +} diff --git a/spring-petclinic-microservices/spring-petclinic-visits-service/src/main/java/org/springframework/samples/petclinic/visits/model/CosmosProperties.java b/spring-petclinic-microservices/spring-petclinic-visits-service/src/main/java/org/springframework/samples/petclinic/visits/model/CosmosProperties.java new file mode 100644 index 000000000..ad70d03fd --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-visits-service/src/main/java/org/springframework/samples/petclinic/visits/model/CosmosProperties.java @@ -0,0 +1,22 @@ +package org.springframework.samples.petclinic.visits.model; + +import lombok.Getter; +import lombok.Setter; +import org.springframework.boot.context.properties.ConfigurationProperties; + + +@Getter +@Setter +@ConfigurationProperties(prefix = "azure.cosmosdb") +class CosmosProperties { + + private String uri; + + private String key; + + private String secondaryKey; + + private String database; + + private boolean populateQueryMetrics; +} diff --git a/spring-petclinic-microservices/spring-petclinic-visits-service/src/main/java/org/springframework/samples/petclinic/visits/model/Visit.java b/spring-petclinic-microservices/spring-petclinic-visits-service/src/main/java/org/springframework/samples/petclinic/visits/model/Visit.java new file mode 100644 index 000000000..9c91e411c --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-visits-service/src/main/java/org/springframework/samples/petclinic/visits/model/Visit.java @@ -0,0 +1,82 @@ +/* + * Copyright 2002-2017 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.samples.petclinic.visits.model; + +import com.azure.spring.data.cosmos.core.mapping.Container; +import com.azure.spring.data.cosmos.core.mapping.GeneratedValue; +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.NoArgsConstructor; +import org.springframework.data.annotation.Id; + +import javax.persistence.GenerationType; +import javax.persistence.criteria.CriteriaBuilder; +import javax.validation.constraints.Size; +import java.util.Date; + +/** + * Simple JavaBean domain object representing a visit. + * + * @author Ken Krebs + * @author Maciej Szarlinski + */ + +@Container(containerName = "visits") +@Builder(builderMethodName = "visit") +@AllArgsConstructor +@NoArgsConstructor +public class Visit { + + @Id + @GeneratedValue + private String id; + + private String petId; + + @Builder.Default + @JsonFormat(pattern = "yyyy-MM-dd") + private Date visit_date = new Date(); + + @Size(max = 8192) + private String description; + + + public void setId(String id) { + this.id = id; + } + + public String getId() { + return id; + } + + public Date getDate() { + return visit_date; + } + + public String getDescription() { + return description; + } + + public String getPetId() { + return petId; + } + + public void setPetId(final String petId) { + this.petId = petId; + } + +} diff --git a/spring-petclinic-microservices/spring-petclinic-visits-service/src/main/java/org/springframework/samples/petclinic/visits/model/VisitRepository.java b/spring-petclinic-microservices/spring-petclinic-visits-service/src/main/java/org/springframework/samples/petclinic/visits/model/VisitRepository.java new file mode 100644 index 000000000..5037e10cb --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-visits-service/src/main/java/org/springframework/samples/petclinic/visits/model/VisitRepository.java @@ -0,0 +1,43 @@ +/* + * Copyright 2002-2017 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.samples.petclinic.visits.model; + +import com.azure.spring.data.cosmos.repository.CosmosRepository; +import org.springframework.stereotype.Repository; +import reactor.core.publisher.Flux; + +import java.util.Collection; + + +/** + * Repository class for Visit domain objects All method names are compliant with Spring Data naming conventions so this interface can easily be extended for Spring + * Data See here: http://static.springsource.org/spring-data/jpa/docs/current/reference/html/jpa.repositories.html#jpa.query-methods.query-creation + * + * @author Ken Krebs + * @author Juergen Hoeller + * @author Sam Brannen + * @author Michael Isvy + * @author Maciej Szarlinski + */ +@Repository +public interface VisitRepository extends CosmosRepository { + +/* + Flux findByPetId(int petId); + + Flux findByPetIdIn(Collection petIds); +*/ +} diff --git a/spring-petclinic-microservices/spring-petclinic-visits-service/src/main/java/org/springframework/samples/petclinic/visits/model/VisitsAppConfiguration.java b/spring-petclinic-microservices/spring-petclinic-visits-service/src/main/java/org/springframework/samples/petclinic/visits/model/VisitsAppConfiguration.java new file mode 100644 index 000000000..ec971f60b --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-visits-service/src/main/java/org/springframework/samples/petclinic/visits/model/VisitsAppConfiguration.java @@ -0,0 +1,79 @@ +package org.springframework.samples.petclinic.visits.model; +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +/** + * WARNING: MODIFYING THIS FILE WILL REQUIRE CORRESPONDING UPDATES TO README.md FILE. LINE NUMBERS + * ARE USED TO EXTRACT APPROPRIATE CODE SEGMENTS FROM THIS FILE. ADD NEW CODE AT THE BOTTOM TO AVOID CHANGING + * LINE NUMBERS OF EXISTING CODE SAMPLES. + */ + +import com.azure.core.credential.AzureKeyCredential; +import com.azure.cosmos.CosmosClientBuilder; +import com.azure.cosmos.DirectConnectionConfig; +import com.azure.cosmos.GatewayConnectionConfig; +import com.azure.spring.data.cosmos.config.AbstractCosmosConfiguration; +import com.azure.spring.data.cosmos.config.CosmosConfig; +import com.azure.spring.data.cosmos.core.ResponseDiagnostics; +import com.azure.spring.data.cosmos.core.ResponseDiagnosticsProcessor; +import com.azure.spring.data.cosmos.repository.config.EnableCosmosRepositories; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.PropertySource; +import org.springframework.lang.Nullable; +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +@Configuration +@EnableConfigurationProperties(CosmosProperties.class) +@EnableCosmosRepositories +@PropertySource("classpath:application.properties") +public class VisitsAppConfiguration extends AbstractCosmosConfiguration { + + private static final Logger logger = LoggerFactory.getLogger(VisitsAppConfiguration.class); + + @Autowired + private CosmosProperties properties; + + private AzureKeyCredential azureKeyCredential; + + @Bean + public CosmosClientBuilder cosmosClientBuilder() { + this.azureKeyCredential = new AzureKeyCredential(properties.getKey()); + DirectConnectionConfig directConnectionConfig = new DirectConnectionConfig(); + GatewayConnectionConfig gatewayConnectionConfig = new GatewayConnectionConfig(); + return new CosmosClientBuilder() + .endpoint(properties.getUri()) + .credential(azureKeyCredential) + .directMode(directConnectionConfig, gatewayConnectionConfig); + } + + @Override + public CosmosConfig cosmosConfig() { + return CosmosConfig.builder() + .enableQueryMetrics(properties.isPopulateQueryMetrics()) + .responseDiagnosticsProcessor(new ResponseDiagnosticsProcessorImplementation()) + .build(); + } + + + public void switchToSecondaryKey() { + this.azureKeyCredential.update(properties.getSecondaryKey()); + } + + @Override + protected String getDatabaseName() { + return properties.getDatabase(); + } + + private static class ResponseDiagnosticsProcessorImplementation implements ResponseDiagnosticsProcessor { + + @Override + public void processResponseDiagnostics(@Nullable ResponseDiagnostics responseDiagnostics) { + logger.info("Response Diagnostics {}", responseDiagnostics); + } + } +} diff --git a/spring-petclinic-microservices/spring-petclinic-visits-service/src/main/java/org/springframework/samples/petclinic/visits/web/VisitResource.java b/spring-petclinic-microservices/spring-petclinic-visits-service/src/main/java/org/springframework/samples/petclinic/visits/web/VisitResource.java new file mode 100644 index 000000000..00fb1c98b --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-visits-service/src/main/java/org/springframework/samples/petclinic/visits/web/VisitResource.java @@ -0,0 +1,78 @@ +/* + * Copyright 2002-2017 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.samples.petclinic.visits.web; + +import com.azure.cosmos.implementation.guava25.collect.Lists; +import io.micrometer.core.annotation.Timed; +import lombok.RequiredArgsConstructor; +import lombok.Value; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpStatus; +import org.springframework.samples.petclinic.visits.model.Visit; +import org.springframework.samples.petclinic.visits.model.VisitRepository; +import org.springframework.web.bind.annotation.*; +import reactor.core.publisher.Flux; + +import javax.validation.Valid; +import java.util.List; +import java.util.Optional; + +/** + * @author Juergen Hoeller + * @author Ken Krebs + * @author Arjen Poutsma + * @author Michael Isvy + * @author Maciej Szarlinski + */ +@RestController +@RequiredArgsConstructor +@Slf4j +@Timed("petclinic.visit") +class VisitResource { + + private final VisitRepository visitRepository; + + @PostMapping("owners/*/pets/{petId}/visits") + @ResponseStatus(HttpStatus.CREATED) + Visit create( + @Valid @RequestBody Visit visit, + @PathVariable("petId") String petId) { + + visit.setPetId(petId); + log.info("Saving visit {}", visit); + return visitRepository.save(visit); + } + + @GetMapping("owners/*/pets/{petId}/visits") + Optional visits(@PathVariable("petId") int petId) { + return visitRepository.findById(petId); + } + + @GetMapping("pets/visits") + Visits visitsMultiGet(@RequestParam("petId") List petIds) { + Iterable visitIterable = visitRepository.findAllById(petIds); + return new Visits(Lists.newArrayList(visitIterable)); + } + + @Value + static class Visits { + private final List items; + + Visits(List items) { + this.items = items; + } + } +} diff --git a/spring-petclinic-microservices/spring-petclinic-visits-service/src/main/resources/application.properties b/spring-petclinic-microservices/spring-petclinic-visits-service/src/main/resources/application.properties new file mode 100644 index 000000000..00228fd42 --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-visits-service/src/main/resources/application.properties @@ -0,0 +1,18 @@ +azure.keyvault.enabled=true +azure.keyvault.uri=${AZURE_KEYVAULT_URI} +azure.keyvault.client-id=${AZURE_KEYVAULT_CLIENTID} +azure.keyvault.client-key=${AZURE_KEYVAULT_CLIENTKEY} +azure.keyvault.tenant-id=${AZURE_KEYVAULT_TENANTID} +azure.keyvault.token-acquire-timeout-seconds=60 +azure.keyvault.refresh-interval=1800000 +azure.keyvault.secret-keys=cosmosdburi,cosmosdbkey,cosmosdbsecondarykey, redisuri, redispassword + +# Azure cosmos db properties +azure.cosmosdb.uri=${cosmosdburi} +azure.cosmosdb.key=${cosmosdbkey} +azure.cosmosdb.secondaryKey=${cosmosdbsecondarykey} +azure.cosmosdb.database=end2endsample +# Populate query metrics +cosmos.queryMetricsEnabled=true + + diff --git a/spring-petclinic-microservices/spring-petclinic-visits-service/src/main/resources/bootstrap.yml b/spring-petclinic-microservices/spring-petclinic-visits-service/src/main/resources/bootstrap.yml new file mode 100644 index 000000000..459638352 --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-visits-service/src/main/resources/bootstrap.yml @@ -0,0 +1,29 @@ +eureka: + client: + register-with-eureka: true + fetch-registry: true +spring: + cloud: + config: + uri: http://localhost:8888 + application: + name: visits-service +--- +spring: + profiles: docker + cloud: + config: + uri: http://config-server:8888 + +--- +spring: + profiles: local + cloud: + config: + uri: http://localhost:8888 +eureka: + client: + serviceUrl: + defaultZone: http://localhost:8761/eureka/ +server: + port: 8082 diff --git a/spring-petclinic-microservices/spring-petclinic-visits-service/src/main/resources/db/hsqldb/data.sql b/spring-petclinic-microservices/spring-petclinic-visits-service/src/main/resources/db/hsqldb/data.sql new file mode 100644 index 000000000..29bc762e6 --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-visits-service/src/main/resources/db/hsqldb/data.sql @@ -0,0 +1,4 @@ +INSERT INTO visits VALUES (1, 7, '2013-01-01', 'rabies shot'); +INSERT INTO visits VALUES (2, 8, '2013-01-02', 'rabies shot'); +INSERT INTO visits VALUES (3, 8, '2013-01-03', 'neutered'); +INSERT INTO visits VALUES (4, 7, '2013-01-04', 'spayed'); diff --git a/spring-petclinic-microservices/spring-petclinic-visits-service/src/main/resources/db/hsqldb/schema.sql b/spring-petclinic-microservices/spring-petclinic-visits-service/src/main/resources/db/hsqldb/schema.sql new file mode 100644 index 000000000..9714a5806 --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-visits-service/src/main/resources/db/hsqldb/schema.sql @@ -0,0 +1,10 @@ +DROP TABLE visits IF EXISTS; + +CREATE TABLE visits ( + id INTEGER IDENTITY PRIMARY KEY, + pet_id INTEGER NOT NULL, + visit_date DATE, + description VARCHAR(8192) +); + +CREATE INDEX visits_pet_id ON visits (pet_id); diff --git a/spring-petclinic-microservices/spring-petclinic-visits-service/src/main/resources/db/mysql/data.sql b/spring-petclinic-microservices/spring-petclinic-visits-service/src/main/resources/db/mysql/data.sql new file mode 100644 index 000000000..d57dce6e5 --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-visits-service/src/main/resources/db/mysql/data.sql @@ -0,0 +1,4 @@ +INSERT IGNORE INTO visits VALUES (1, 7, '2010-03-04', 'rabies shot'); +INSERT IGNORE INTO visits VALUES (2, 8, '2011-03-04', 'rabies shot'); +INSERT IGNORE INTO visits VALUES (3, 8, '2009-06-04', 'neutered'); +INSERT IGNORE INTO visits VALUES (4, 7, '2008-09-04', 'spayed'); diff --git a/spring-petclinic-microservices/spring-petclinic-visits-service/src/main/resources/db/mysql/schema.sql b/spring-petclinic-microservices/spring-petclinic-visits-service/src/main/resources/db/mysql/schema.sql new file mode 100644 index 000000000..65c448c69 --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-visits-service/src/main/resources/db/mysql/schema.sql @@ -0,0 +1,12 @@ +CREATE DATABASE IF NOT EXISTS petclinic; +GRANT ALL PRIVILEGES ON petclinic.* TO pc@localhost IDENTIFIED BY 'pc'; + +USE petclinic; + +CREATE TABLE IF NOT EXISTS visits ( + id INT(4) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, + pet_id INT(4) UNSIGNED NOT NULL, + visit_date DATE, + description VARCHAR(8192), + FOREIGN KEY (pet_id) REFERENCES pets(id) +) engine=InnoDB; diff --git a/spring-petclinic-microservices/spring-petclinic-visits-service/src/main/resources/logback-spring.xml b/spring-petclinic-microservices/spring-petclinic-visits-service/src/main/resources/logback-spring.xml new file mode 100644 index 000000000..5d03f7941 --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-visits-service/src/main/resources/logback-spring.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/spring-petclinic-microservices/spring-petclinic-visits-service/src/test/java/org/springframework/samples/petclinic/visits/web/VisitResourceTest.java b/spring-petclinic-microservices/spring-petclinic-visits-service/src/test/java/org/springframework/samples/petclinic/visits/web/VisitResourceTest.java new file mode 100644 index 000000000..645add0e7 --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-visits-service/src/test/java/org/springframework/samples/petclinic/visits/web/VisitResourceTest.java @@ -0,0 +1,55 @@ +package org.springframework.samples.petclinic.visits.web; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.OverrideAutoConfiguration; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.samples.petclinic.visits.model.Visit; +import org.springframework.samples.petclinic.visits.model.VisitRepository; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import org.springframework.test.web.servlet.MockMvc; +import reactor.core.publisher.Flux; + +import java.util.Arrays; +import java.util.List; + +import static java.util.Arrays.asList; +import static org.mockito.Mockito.when; +import static org.springframework.samples.petclinic.visits.model.Visit.visit; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@ExtendWith(SpringExtension.class) +@AutoConfigureMockMvc +@SpringBootTest +@OverrideAutoConfiguration(enabled = true) +@ActiveProfiles("test") +public class VisitResourceTest { + + @Autowired + MockMvc mvc; + + @MockBean + VisitRepository visitRepository; + + @Test + void shouldFetchVisits() throws Exception { + + List list = Arrays.asList(visit().id("1").petId("111").build(), + visit().id("2").petId("222").build() + ); + when(visitRepository.findAllById(asList(111, 222))).thenReturn(list); + + mvc.perform(get("/pets/visits?petId=111,222")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.items[0].id").value("1")) + .andExpect(jsonPath("$.items[1].id").value("2")) + .andExpect(jsonPath("$.items[0].petId").value("111")) + .andExpect(jsonPath("$.items[1].petId").value("222")); + } +} diff --git a/spring-petclinic-microservices/spring-petclinic-visits-service/src/test/resources/application-test.yml b/spring-petclinic-microservices/spring-petclinic-visits-service/src/test/resources/application-test.yml new file mode 100644 index 000000000..07e39e627 --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-visits-service/src/test/resources/application-test.yml @@ -0,0 +1,8 @@ + +spring: + datasource: + schema: classpath*:db/hsqldb/schema.sql + data: classpath*:db/hsqldb/data.sql + +logging.level.org.springframework: INFO + diff --git a/spring-petclinic-microservices/spring-petclinic-visits-service/src/test/resources/bootstrap-test.yml b/spring-petclinic-microservices/spring-petclinic-visits-service/src/test/resources/bootstrap-test.yml new file mode 100644 index 000000000..cb8e4f5f7 --- /dev/null +++ b/spring-petclinic-microservices/spring-petclinic-visits-service/src/test/resources/bootstrap-test.yml @@ -0,0 +1,2 @@ +spring.cloud.config.enabled: false +eureka.client.enabled: false