diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..402d4f3 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,4 @@ +[submodule "full-apps/catalog-microservice"] + path = full-apps/catalog-microservice + url = https://github.com/seedstack/catalog-microservice-sample + branch = master diff --git a/.travis.yml b/.travis.yml index 9ead01f..9864e78 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,11 +2,40 @@ sudo: false language: java -jdk: - - oraclejdk8 - - oraclejdk9 +services: + - docker + +env: + global: + - COMMIT=${TRAVIS_COMMIT::8} +jdk: + - openjdk8 + - openjdk9 + - openjdk10 + - openjdk11 + - openjdk12 + - openjdk13 + - openjdk14 + - openjdk15 + - openjdk16 + - openjdk17 + cache: directories: - "$HOME/.m2/repository" +script: + - if [[ $TRAVIS_PULL_REQUEST = false ]] && [[ $TRAVIS_BRANCH = master ]] && [[ $TRAVIS_JDK_VERSION = openjdk15 ]]; then GOAL=deploy; else GOAL=package; fi + - echo $GOAL + - mvn -U $GOAL + - docker build -t sample-oauth-server:$COMMIT ./addons/oauth + +deploy: + provider: script + script: bash ./addons/oauth/deploy.sh + on: + all_branches: true + jdk: openjdk11 + +after_success: mvn -q coveralls:report -DrepoToken=$COVERALLS_TOKEN diff --git a/addons/README.md b/addons/README.md index 5ccfb0d..772c7ac 100644 --- a/addons/README.md +++ b/addons/README.md @@ -7,6 +7,8 @@ typical use case in test class(es). | Sample | Directory | Demonstrated feature(s) | |---|---|---| +| Camel | [camel](https://github.com/seedstack/samples/tree/master/addons/camel) | Apache Camel integration | +| OAuth | [oauth](https://github.com/seedstack/samples/tree/master/addons/oauth) | OAuth security addon samples | | Spring batch | [spring-batch](https://github.com/seedstack/samples/tree/master/addons/spring-batch) | Spring Batch integration | | Spring bridge | [spring-bridge](https://github.com/seedstack/samples/tree/master/addons/spring-bridge) | Injection bridge between SeedStack and Spring | | W20 bridge | [w20-bridge](https://github.com/seedstack/samples/tree/master/addons/w20-bridge) | Automatic integration of [W20](https://w20-framework.github.io/) frontend framework | diff --git a/basics/web/LICENSE b/addons/camel/LICENSE similarity index 100% rename from basics/web/LICENSE rename to addons/camel/LICENSE diff --git a/addons/camel/README.md b/addons/camel/README.md new file mode 100644 index 0000000..a7ff296 --- /dev/null +++ b/addons/camel/README.md @@ -0,0 +1,34 @@ +# SeedStack Camel addon sample + +This sample demonstrates how to use Apache Camel addon for Seedstack + +## Build + +```bash +mvn clean package +``` + +## Run + +Execute tests located in `src/test/java` in your IDE, or with Maven: + +```bash +mvn clean verify +``` + +The integration test gisves an example of how to set a basic Camel route. +This route copies files from one folder to another using the addon. + +## Sample + +This sample is a simple web-application exposing a "/person" resource as a Web service. +Sending a POST request to /person/queue will send a Person object both +- Through a standard JMS route +- Through a Camel Managed JMS route + +The JSM component, creating the route with a JMS queue is initialized using an implementation of CamelContextInitializer + +## Copyright and license + +This source code is copyrighted by [The SeedStack Authors](https://github.com/seedstack/seedstack/blob/master/AUTHORS) and +released under the terms of the [Mozilla Public License 2.0](https://www.mozilla.org/MPL/2.0/). diff --git a/addons/camel/pom.xml b/addons/camel/pom.xml new file mode 100644 index 0000000..28bdd95 --- /dev/null +++ b/addons/camel/pom.xml @@ -0,0 +1,132 @@ + + + + 4.0.0 + + + org.seedstack.samples + samples + 20.11-SNAPSHOT + ../../pom.xml + + + camel-sample + + + 3.1.0 + 2.6 + 5.15.9 + 1.1-rev-1 + + + + + + org.seedstack + seedstack-maven-plugin + + + build-capsule + + package + + + + + + + + + org.seedstack.seed + seed-core + + + org.seedstack.seed + seed-security-core + + + org.seedstack.seed + seed-web-core + + + org.seedstack.seed + seed-web-security + + + org.seedstack.seed + seed-rest-jersey2 + + + org.seedstack.seed + seed-web-undertow + + + org.seedstack.business + business-core + + + org.seedstack.addons.modelmapper + modelmapper + + + ch.qos.logback + logback-classic + ${logback.version} + + + + + org.seedstack.addons.jms + jms + + + javax.jms + jms-api + ${jms-api.version} + + + org.apache.activemq + activemq-camel + ${activemq.version} + + + org.apache.activemq + activemq-broker + ${activemq.version} + + + + + org.seedstack.addons.camel + camel + + + + + org.apache.camel + camel-jms + ${camel.version} + + + commons-io + commons-io + ${commons-io.version} + + + + + org.seedstack.seed + seed-testing-junit4 + test + + + diff --git a/addons/camel/src/main/java/org/seedstack/samples/camel/application/error/CamelSampleErrorCode.java b/addons/camel/src/main/java/org/seedstack/samples/camel/application/error/CamelSampleErrorCode.java new file mode 100644 index 0000000..30c74cd --- /dev/null +++ b/addons/camel/src/main/java/org/seedstack/samples/camel/application/error/CamelSampleErrorCode.java @@ -0,0 +1,15 @@ +/* + * Copyright © 2013-2020, The SeedStack authors + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +package org.seedstack.samples.camel.application.error; + +import org.seedstack.shed.exception.ErrorCode; + +public enum CamelSampleErrorCode implements ErrorCode { + ERR_JMS +} diff --git a/addons/camel/src/main/java/org/seedstack/samples/camel/application/message/MessageService.java b/addons/camel/src/main/java/org/seedstack/samples/camel/application/message/MessageService.java new file mode 100644 index 0000000..470d3e5 --- /dev/null +++ b/addons/camel/src/main/java/org/seedstack/samples/camel/application/message/MessageService.java @@ -0,0 +1,25 @@ +/* + * Copyright © 2013-2020, The SeedStack authors + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +package org.seedstack.samples.camel.application.message; + +import org.seedstack.business.Service; +import org.seedstack.samples.camel.domain.model.person.Person; + +@Service +public interface MessageService { + public static final String QUEUE_STD_NAME="queue1"; + public static final String QUEUE_CAMEL_NAME="camelQueue"; + /** + * Sends a person to the Message Queue + * @param person the person item to send + * @param queueName the queue name + */ + void sendPersonMessage(Person person, String queueName); + +} diff --git a/addons/camel/src/main/java/org/seedstack/samples/camel/application/message/MessageServiceImpl.java b/addons/camel/src/main/java/org/seedstack/samples/camel/application/message/MessageServiceImpl.java new file mode 100644 index 0000000..4a20b0c --- /dev/null +++ b/addons/camel/src/main/java/org/seedstack/samples/camel/application/message/MessageServiceImpl.java @@ -0,0 +1,47 @@ +/* + * Copyright © 2013-2020, The SeedStack authors + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +package org.seedstack.samples.camel.application.message; + +import org.seedstack.jms.JmsConnection; +import org.seedstack.samples.camel.application.error.CamelSampleErrorCode; +import org.seedstack.samples.camel.domain.model.person.Person; +import org.seedstack.seed.Logging; +import org.seedstack.seed.SeedException; +import org.seedstack.seed.transaction.Transactional; +import org.slf4j.Logger; + +import javax.inject.Inject; +import javax.jms.*; + +public class MessageServiceImpl implements MessageService { + + @Logging + private Logger logger; + + @Inject + private Session session; + + @Transactional + @JmsConnection("connection1") + @Override + public void sendPersonMessage(Person person, String queueName) { + try { + logger.info("Adding person to JMS Queue"); + Destination queue = session.createQueue(queueName); + MessageProducer producer = session.createProducer(queue); + ObjectMessage message = session.createObjectMessage(); + message.setObject(person); + producer.send(message); + } + catch (JMSException jsmE){ + throw SeedException.wrap(jsmE, CamelSampleErrorCode.ERR_JMS); + } + } + +} diff --git a/addons/camel/src/main/java/org/seedstack/samples/camel/application/message/PersonMessageListener.java b/addons/camel/src/main/java/org/seedstack/samples/camel/application/message/PersonMessageListener.java new file mode 100644 index 0000000..abb751c --- /dev/null +++ b/addons/camel/src/main/java/org/seedstack/samples/camel/application/message/PersonMessageListener.java @@ -0,0 +1,36 @@ +/* + * Copyright © 2013-2020, The SeedStack authors + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +package org.seedstack.samples.camel.application.message; + +import org.seedstack.jms.JmsMessageListener; +import org.seedstack.samples.camel.domain.model.person.Person; +import org.seedstack.seed.Logging; +import org.slf4j.Logger; + +import javax.jms.Message; +import javax.jms.MessageListener; +import javax.jms.ObjectMessage; + +@JmsMessageListener(connection = "connection1", destinationName = "queue1") +public class PersonMessageListener implements MessageListener { + @Logging + private Logger logger; + + + + @Override + public void onMessage(Message message) { + if (message instanceof ObjectMessage) { + logger.info("Message received !"); + } else { + logger.warn("Unsupported message type"); + } + + } +} diff --git a/addons/camel/src/main/java/org/seedstack/samples/camel/application/routes/PersonRouteBuilder.java b/addons/camel/src/main/java/org/seedstack/samples/camel/application/routes/PersonRouteBuilder.java new file mode 100644 index 0000000..d37bb3d --- /dev/null +++ b/addons/camel/src/main/java/org/seedstack/samples/camel/application/routes/PersonRouteBuilder.java @@ -0,0 +1,29 @@ +/* + * Copyright © 2013-2020, The SeedStack authors + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +package org.seedstack.samples.camel.application.routes; + +import org.apache.camel.Processor; +import org.apache.camel.builder.RouteBuilder; +import org.seedstack.samples.camel.application.routes.processors.PersonQueueProcessor; + +import javax.inject.Inject; + +/** + * This Camel route uses Camel JMS component to add a person to the repository + */ +public class PersonRouteBuilder extends RouteBuilder { + + @Inject + private PersonQueueProcessor processor; + + @Override + public void configure() throws Exception { + from("jmsComponent:queue:camelQueue").process(processor).to("mock:noway"); + } +} diff --git a/addons/camel/src/main/java/org/seedstack/samples/camel/application/routes/endPoint/AddPersonEndPoint.java b/addons/camel/src/main/java/org/seedstack/samples/camel/application/routes/endPoint/AddPersonEndPoint.java new file mode 100644 index 0000000..f407494 --- /dev/null +++ b/addons/camel/src/main/java/org/seedstack/samples/camel/application/routes/endPoint/AddPersonEndPoint.java @@ -0,0 +1,30 @@ +/* + * Copyright © 2013-2020, The SeedStack authors + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +package org.seedstack.samples.camel.application.routes.endPoint; + +import org.apache.camel.Consumer; +import org.apache.camel.Endpoint; +import org.apache.camel.Processor; +import org.apache.camel.Producer; +import org.apache.camel.support.DefaultEndpoint; +import org.seedstack.camel.CamelEndpoint; + +@CamelEndpoint(endPointUri = "person:addPerson") +public class AddPersonEndPoint extends DefaultEndpoint { + + @Override + public Producer createProducer() throws Exception { + return null; + } + + @Override + public Consumer createConsumer(Processor processor) throws Exception { + return null; + } +} diff --git a/addons/camel/src/main/java/org/seedstack/samples/camel/application/routes/initializer/CamelApplicationInitializer.java b/addons/camel/src/main/java/org/seedstack/samples/camel/application/routes/initializer/CamelApplicationInitializer.java new file mode 100644 index 0000000..b210c2a --- /dev/null +++ b/addons/camel/src/main/java/org/seedstack/samples/camel/application/routes/initializer/CamelApplicationInitializer.java @@ -0,0 +1,34 @@ +/* + * Copyright © 2013-2020, The SeedStack authors + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +package org.seedstack.samples.camel.application.routes.initializer; + +import org.apache.activemq.ActiveMQConnectionFactory; +import org.apache.camel.CamelContext; +import org.apache.camel.component.jms.JmsComponent; +import org.seedstack.camel.CamelContextInitializer; +import org.seedstack.jms.spi.JmsFactory; +import org.seedstack.seed.Logging; +import org.slf4j.Logger; + +import javax.inject.Inject; +import javax.jms.ConnectionFactory; +import javax.jms.Session; + +public class CamelApplicationInitializer implements CamelContextInitializer { + + @Logging + private Logger logger; + + @Override + public void initialize(CamelContext camelContext) { + logger.info("Camel plugin sample - Initializing camel context"); + ConnectionFactory connectionFactory = new ActiveMQConnectionFactory("vm://localhost?broker.persistent=false"); + camelContext.addComponent("jmsComponent", JmsComponent.jmsComponentAutoAcknowledge(connectionFactory)); + } +} diff --git a/addons/camel/src/main/java/org/seedstack/samples/camel/application/routes/processors/PersonQueueProcessor.java b/addons/camel/src/main/java/org/seedstack/samples/camel/application/routes/processors/PersonQueueProcessor.java new file mode 100644 index 0000000..66fefbe --- /dev/null +++ b/addons/camel/src/main/java/org/seedstack/samples/camel/application/routes/processors/PersonQueueProcessor.java @@ -0,0 +1,28 @@ +/* + * Copyright © 2013-2020, The SeedStack authors + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +package org.seedstack.samples.camel.application.routes.processors; + +import org.apache.camel.Exchange; +import org.apache.camel.Processor; +import org.seedstack.seed.Logging; +import org.slf4j.Logger; + +/** + * Camel processor + */ +public class PersonQueueProcessor implements Processor { + + @Logging + Logger logger; + + @Override + public void process(Exchange exchange) throws Exception { + logger.info("Exchange treated in processor"); + } +} diff --git a/addons/camel/src/main/java/org/seedstack/samples/camel/domain/model/person/Person.java b/addons/camel/src/main/java/org/seedstack/samples/camel/domain/model/person/Person.java new file mode 100644 index 0000000..7bc814c --- /dev/null +++ b/addons/camel/src/main/java/org/seedstack/samples/camel/domain/model/person/Person.java @@ -0,0 +1,47 @@ +/* + * Copyright © 2013-2020, The SeedStack authors + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +package org.seedstack.samples.camel.domain.model.person; + +import org.seedstack.business.domain.BaseAggregateRoot; + +import java.io.Serializable; + +public class Person extends BaseAggregateRoot implements Serializable { + private final PersonId id; + private String firstName; + private String lastName; + + public Person(PersonId id) { + this.id = id; + } + + @Override + public PersonId getId() { + return id; + } + + public String getFirstName() { + return firstName; + } + + public String getLastName() { + return lastName; + } + + public void changeName(String firstName, String lastName) { + if (firstName == null || firstName.isEmpty()) { + throw new IllegalArgumentException("First name is missing"); + } + if (lastName == null || lastName.isEmpty()) { + throw new IllegalArgumentException("Last name is missing"); + } + this.firstName = firstName; + this.lastName = lastName; + } +} diff --git a/addons/camel/src/main/java/org/seedstack/samples/camel/domain/model/person/PersonId.java b/addons/camel/src/main/java/org/seedstack/samples/camel/domain/model/person/PersonId.java new file mode 100644 index 0000000..48f12ff --- /dev/null +++ b/addons/camel/src/main/java/org/seedstack/samples/camel/domain/model/person/PersonId.java @@ -0,0 +1,26 @@ +/* + * Copyright © 2013-2020, The SeedStack authors + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +package org.seedstack.samples.camel.domain.model.person; + +import org.seedstack.business.domain.BaseValueObject; + +/** + * Person value object + */ +public class PersonId extends BaseValueObject { + + private final String email; + public PersonId(String email) { + this.email = email; + } + public String getEmail() { + return email; + } + +} diff --git a/addons/camel/src/main/java/org/seedstack/samples/camel/infrastructure/SampleDataGenerator.java b/addons/camel/src/main/java/org/seedstack/samples/camel/infrastructure/SampleDataGenerator.java new file mode 100644 index 0000000..8e3df6a --- /dev/null +++ b/addons/camel/src/main/java/org/seedstack/samples/camel/infrastructure/SampleDataGenerator.java @@ -0,0 +1,41 @@ +/* + * Copyright © 2013-2020, The SeedStack authors + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +package org.seedstack.samples.camel.infrastructure; + +import javax.inject.Inject; + +import org.seedstack.business.domain.Repository; +import org.seedstack.business.util.inmemory.InMemory; +import org.seedstack.samples.camel.domain.model.person.Person; +import org.seedstack.samples.camel.domain.model.person.PersonId; +import org.seedstack.seed.LifecycleListener; +import org.seedstack.seed.Logging; +import org.slf4j.Logger; + +public class SampleDataGenerator implements LifecycleListener { + @Inject + @InMemory + private Repository personRepository; + @Logging + private Logger logger; + + @Override + public void started() { + logger.info("Adding data to repository"); + personRepository.addOrUpdate(create("bill.evans@some.org", "Bill", "EVANS")); + personRepository.addOrUpdate(create("ella.fitzgerald@some.org", "Ella", "FITZGERALD")); + personRepository.addOrUpdate(create("miles.davis@some.org", "Miles", "DAVIS")); + } + + private Person create(String email, String firstName, String lastName) { + Person person = new Person(new PersonId(email)); + person.changeName(firstName, lastName); + return person; + } +} diff --git a/addons/camel/src/main/java/org/seedstack/samples/camel/interfaces/person/AddPersonResponse.java b/addons/camel/src/main/java/org/seedstack/samples/camel/interfaces/person/AddPersonResponse.java new file mode 100644 index 0000000..9cbe754 --- /dev/null +++ b/addons/camel/src/main/java/org/seedstack/samples/camel/interfaces/person/AddPersonResponse.java @@ -0,0 +1,30 @@ +/* + * Copyright © 2013-2020, The SeedStack authors + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +package org.seedstack.samples.camel.interfaces.person; + +/** + * Response for adding a person to the repository + */ +public class AddPersonResponse { + public static final String STATUS_OK="OK"; + public static final String STATUS_FAIL="Fail"; + + private String status; + + public AddPersonResponse(String status){ + if(!status.equals(STATUS_FAIL) && !(status.equals(STATUS_OK))){ + throw new IllegalArgumentException("Incorrect status"); + } + this.status=status; + } + + public String getStatus() { + return status; + } +} diff --git a/addons/camel/src/main/java/org/seedstack/samples/camel/interfaces/person/PersonRepresentation.java b/addons/camel/src/main/java/org/seedstack/samples/camel/interfaces/person/PersonRepresentation.java new file mode 100644 index 0000000..5b751cc --- /dev/null +++ b/addons/camel/src/main/java/org/seedstack/samples/camel/interfaces/person/PersonRepresentation.java @@ -0,0 +1,68 @@ +/* + * Copyright © 2013-2020, The SeedStack authors + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +package org.seedstack.samples.camel.interfaces.person; + +import com.google.common.base.Strings; +import org.seedstack.samples.camel.domain.model.person.Person; +import org.seedstack.samples.camel.domain.model.person.PersonId; + +/** + * Represents a person + */ +public class PersonRepresentation { + private String mail; + private String firstName; + private String lastName; + + public PersonRepresentation(Person person){ + this.mail= person.getId().getEmail(); + this.firstName=person.getFirstName(); + this.lastName=person.getLastName(); + } + + /** + * Empty constructor + */ + public PersonRepresentation(){ + } + + public Person asPerson(){ + if(Strings.isNullOrEmpty(mail) || Strings.isNullOrEmpty(firstName) || Strings.isNullOrEmpty(lastName)){ + throw new UnsupportedOperationException("Person fields are missing"); + } + PersonId identifier = new PersonId(mail); + Person person = new Person(identifier); + person.changeName(firstName, lastName); + return person; + } + + public void setMail(String mail) { + this.mail = mail; + } + + public void setFirstName(String firstName) { + this.firstName = firstName; + } + + public void setLastName(String lastName) { + this.lastName = lastName; + } + + public String getMail() { + return mail; + } + + public String getFirstName() { + return firstName; + } + + public String getLastName() { + return lastName; + } +} diff --git a/addons/camel/src/main/java/org/seedstack/samples/camel/interfaces/person/PersonResource.java b/addons/camel/src/main/java/org/seedstack/samples/camel/interfaces/person/PersonResource.java new file mode 100644 index 0000000..58b50ad --- /dev/null +++ b/addons/camel/src/main/java/org/seedstack/samples/camel/interfaces/person/PersonResource.java @@ -0,0 +1,80 @@ +/* + * Copyright © 2013-2020, The SeedStack authors + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +package org.seedstack.samples.camel.interfaces.person; + +import org.seedstack.business.domain.Repository; +import org.seedstack.business.specification.Specification; +import org.seedstack.business.util.inmemory.InMemory; +import org.seedstack.samples.camel.application.message.MessageService; +import org.seedstack.samples.camel.application.message.MessageServiceImpl; +import org.seedstack.samples.camel.domain.model.person.Person; +import org.seedstack.samples.camel.domain.model.person.PersonId; +import org.seedstack.seed.SeedException; + +import javax.inject.Inject; +import javax.ws.rs.*; +import javax.ws.rs.core.MediaType; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +/** + * person resources - provides an API for Person + */ +@Path("person") +public class PersonResource { + + @Inject + @InMemory + private Repository personRepository; + @Inject + private MessageService messageService; + + @GET + @Produces(MediaType.APPLICATION_JSON) + public List getFullPersonList(){ + List finalList= new ArrayList<>(); + personRepository.get(getAllPersonSpecification()).forEach(person -> { + finalList.add(new PersonRepresentation(person)); + }); + return finalList; + } + + @POST + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + public AddPersonResponse addPerson(PersonRepresentation personRepresentation){ + try { + personRepository.addOrUpdate(personRepresentation.asPerson()); + return new AddPersonResponse(AddPersonResponse.STATUS_OK); + } + catch (Exception e){ + return new AddPersonResponse(AddPersonResponse.STATUS_FAIL); + } + } + + @POST + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + @Path("/queue") + public AddPersonResponse messageAddPerson(PersonRepresentation personRepresentation){ + try { + messageService.sendPersonMessage(personRepresentation.asPerson(), MessageService.QUEUE_STD_NAME); + messageService.sendPersonMessage(personRepresentation.asPerson(), MessageService.QUEUE_CAMEL_NAME); + return new AddPersonResponse(AddPersonResponse.STATUS_OK); + } + catch(Exception e){ + return new AddPersonResponse(AddPersonResponse.STATUS_FAIL); + } + } + + private Specification getAllPersonSpecification(){ + return personRepository.getSpecificationBuilder().of(Person.class).all().build(); + } +} diff --git a/addons/camel/src/main/java/org/seedstack/samples/camel/routes/FileCopyRouteBuilder.java b/addons/camel/src/main/java/org/seedstack/samples/camel/routes/FileCopyRouteBuilder.java new file mode 100644 index 0000000..7737b55 --- /dev/null +++ b/addons/camel/src/main/java/org/seedstack/samples/camel/routes/FileCopyRouteBuilder.java @@ -0,0 +1,24 @@ +/* + * Copyright © 2013-2020, The SeedStack authors + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.seedstack.samples.camel.routes; + +import org.apache.camel.builder.RouteBuilder; +import org.seedstack.seed.Configuration; + +public class FileCopyRouteBuilder extends RouteBuilder { + + @Configuration("sample.routeFc.from") + private String origin; + @Configuration("sample.routeFc.to") + private String destination; + + @Override + public void configure() throws Exception { + from(origin).to(destination); + } +} diff --git a/addons/camel/src/main/resources/META-INF/errors/org.seedstack.samples.camel.application.error.CamelSampleErrorCode.properties b/addons/camel/src/main/resources/META-INF/errors/org.seedstack.samples.camel.application.error.CamelSampleErrorCode.properties new file mode 100644 index 0000000..b7a8220 --- /dev/null +++ b/addons/camel/src/main/resources/META-INF/errors/org.seedstack.samples.camel.application.error.CamelSampleErrorCode.properties @@ -0,0 +1,2 @@ +ERR_JMS.message = JMS sending/receiving failure +ERR_JMS.fix = Check your JSM configuration \ No newline at end of file diff --git a/addons/camel/src/main/resources/META-INF/resources/index.html b/addons/camel/src/main/resources/META-INF/resources/index.html new file mode 100644 index 0000000..e2def40 --- /dev/null +++ b/addons/camel/src/main/resources/META-INF/resources/index.html @@ -0,0 +1,13 @@ + + + + +This is the Seedstack camel addon sample + + \ No newline at end of file diff --git a/addons/camel/src/main/resources/application.yaml b/addons/camel/src/main/resources/application.yaml new file mode 100644 index 0000000..8d25746 --- /dev/null +++ b/addons/camel/src/main/resources/application.yaml @@ -0,0 +1,29 @@ +application: + id: camel-sample + basePackages : org.seedstack.samples.camel + +web : + server: + port: 80 + static: + enabled: true + +sample: + folder: + tempFolder: ${sys.java\.io\.tmpdir}/Camel + origin: ${sample.folder.tempFolder}/inbox + destination: ${sample.folder.tempFolder}/outbox + routeFc: + from: file:${sample.folder.origin}?noop=true + to: file:${sample.folder.destination} + +jms: + connectionFactories: + connectionFactory1: + vendorClass: org.apache.activemq.ActiveMQConnectionFactory + vendorProperties: + brokerURL: vm://localhost?broker.persistent=false + connections: + connection1: + connectionFactory: connectionFactory1 + reconnectionDelay: 50 \ No newline at end of file diff --git a/addons/camel/src/test/java/org/seedstack/samples/camel/FileCopyRouteIT.java b/addons/camel/src/test/java/org/seedstack/samples/camel/FileCopyRouteIT.java new file mode 100644 index 0000000..f0371bf --- /dev/null +++ b/addons/camel/src/test/java/org/seedstack/samples/camel/FileCopyRouteIT.java @@ -0,0 +1,112 @@ +/* + * Copyright © 2013-2020, The SeedStack authors + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.seedstack.samples.camel; + +import org.apache.commons.io.FileUtils; +import org.junit.Assert; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.junit.runner.RunWith; +import org.seedstack.seed.Configuration; +import org.seedstack.seed.Logging; +import org.seedstack.seed.testing.junit4.SeedITRunner; +import org.slf4j.Logger; + +import java.io.File; +import java.io.IOException; +import java.nio.charset.Charset; +import java.nio.file.Files; + +@RunWith(SeedITRunner.class) +public class FileCopyRouteIT { + private static final String DATA_CONTENT="Camel Addon data content"; + @Logging + private Logger logger; + + @Configuration("sample.folder.origin") + private File originFolder; + @Configuration("sample.folder.destination") + private File destinationFolder; + @Configuration("sample.folder.tempFolder") + private File rootTemporaryFolder; + + @Rule + public TemporaryFolder tempFolder = new TemporaryFolder(); + + + /** + * The file copy route is defined in {@link org.seedstack.samples.camel.routes.FileCopyRouteBuilder} + * and parametrized with configuration values. + * Seedstack adds this route builder to the Camel context and starts it.
+ *
+ * This test copies a file to the origin folder and check that a copy of this file is present in the destination folder + */ + @Test + public void testFileCopyRoute() throws IOException, InterruptedException { + logger.info("SMO test : {}", System.getProperty("java.io.tmpdir")); + cleanTestDirectories(); + File dataFile=createAndFillDataFile(); + + try { + //Copy the file to the origin folder + logger.info("Copying file to the origin folder"); + FileUtils.copyFileToDirectory(dataFile, originFolder); + //Wait a little for the Camel route to proceed its behaviour + Thread.sleep(1000); + File copiedFile = FileUtils.getFile(destinationFolder, dataFile.getName()); + Assert.assertTrue("The destination file does not exist", copiedFile.exists()); + logger.info("The file is copied to the destination folder"); + Assert.assertTrue("The data of origin and destination files do not match !", FileUtils.contentEquals(dataFile, copiedFile)); + logger.info("The file content has been copied"); + } + finally { + cleanTestDirectories(); + deleteTempDirs(); + } + } + private File createAndFillDataFile(){ + logger.info("Creating data file"); + String fileName="CamelFileRouteData"+System.currentTimeMillis(); + File dataFile=null; + try { + dataFile=tempFolder.newFile(fileName); + FileUtils.write(dataFile, DATA_CONTENT, Charset.defaultCharset()); + } + catch (IOException ioe){ + Assert.fail("IOException while creating and filling data file"); + } + return dataFile; + } + + private void cleanTestDirectories(){ + try { + //Preparing test dirs + if(!originFolder.exists()){ + Files.createDirectories(originFolder.toPath()); + } + if(!destinationFolder.exists()){ + Files.createDirectories(destinationFolder.toPath()); + } + logger.info("Cleaning test directories"); + FileUtils.cleanDirectory(originFolder); + FileUtils.cleanDirectory(destinationFolder); + } + catch(IOException ioe){ + Assert.fail("IOException while cleaning test directories"); + } + } + + private void deleteTempDirs(){ + try { + FileUtils.deleteDirectory(rootTemporaryFolder); + } catch (IOException e) { + logger.warn("Unable to delete directory : {} : {}"+rootTemporaryFolder.getPath(),e); + } + } +} diff --git a/addons/camel/src/test/resources/application.yaml b/addons/camel/src/test/resources/application.yaml new file mode 100644 index 0000000..d2f44a4 --- /dev/null +++ b/addons/camel/src/test/resources/application.yaml @@ -0,0 +1,16 @@ +# +# Copyright © 2013-2020, The SeedStack authors +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# + +sample: + folder: + tempFolder: ${sys.java\.io\.tmpdir}/Camel + origin: ${sample.folder.tempFolder}/inbox + destination: ${sample.folder.tempFolder}/outbox + route: + from: file:${sample.folder.origin}?noop=true + to: file:${sample.folder.destination} diff --git a/addons/oauth/Dockerfile b/addons/oauth/Dockerfile new file mode 100644 index 0000000..3342adc --- /dev/null +++ b/addons/oauth/Dockerfile @@ -0,0 +1,6 @@ +FROM jboss/keycloak +ENV KEYCLOAK_USER=admin +ENV KEYCLOAK_PASSWORD=admin +ENV KEYCLOAK_IMPORT=/tmp/SeedStackRealm.json +EXPOSE 8080 +COPY SeedStackRealm.json /tmp diff --git a/addons/oauth/README.md b/addons/oauth/README.md new file mode 100644 index 0000000..b8aec3f --- /dev/null +++ b/addons/oauth/README.md @@ -0,0 +1,47 @@ +# SeedStack OAuth addon samples + +The samples in this directory demonstrate how to configure and use SeedStack's OAuth addon on standard use cases. + +## Prerequisites + +The OAuth addon samples use a pre-configured authentication and access management demo server. + +We use [docker](https://www.docker.com/) to build and run this authorization server from within a container, please make +sure docker is installed on your workstation and docker daemon is running. + +### Building the auth server docker image + +In this directory stand the required files to build the auth server image : + +* `Dockerfile`: a ready to use docker image building script. +* `SeedStackRealm.json`: file containing the necessary configuration for running the samples (Realm / clients / Users definition) + +There is no need to build the image yourself as it is available on the **Docker Hub**, but you can with the following +command: + +``` +docker build -t "seedstack/auth-server:1.0" . +``` + +### Running the auth server + +The auth server is listening on the 8080 port. Before running it, ensure that no other application is listening on the +same port. To enable the auth server, just start the container by running: + +``` +docker run -p 8080:8080 seedstack/auth-server:1.0 +``` + +To check that the auth server is running correctly, browse http://localhost:8080/auth, you should see a Keycloack +welcome page. + +Ensure that the setting have been correctly imported by connecting to the administration console with the admin/admin +credentials. You should see the "SeedSamplesRealm" configured realm. + +## Use cases + +The following directories show different use cases of the OAuth addon usage : + +* [auth-code-flow](https://github.com/seedstack/samples/tree/master/addons/oauth/auth-code-flow) : Use case sample for the **Authorization code flow** usage. +* [auth-code-pkce-flow](https://github.com/seedstack/samples/tree/master/addons/oauth/auth-code-pkce-flow) : use case for the **Authorization Code Flow with Proof Key for Code Exchange (PKCE)** usage. +* [client-credentials-flow-cli](https://github.com/seedstack/samples/tree/master/addons/oauth/client-credentials-flow-cli) and [client-credentials-flow-server](https://github.com/seedstack/samples/tree/master/addons/oauth/client-credentials-flow-server) : use case for the **Client Credentials Flow** usage. \ No newline at end of file diff --git a/addons/oauth/SeedStackRealm.json b/addons/oauth/SeedStackRealm.json new file mode 100644 index 0000000..fcbb1bd --- /dev/null +++ b/addons/oauth/SeedStackRealm.json @@ -0,0 +1,2411 @@ +{ + "id": "SeedSamplesRealm", + "realm": "SeedSamplesRealm", + "displayName": "Seed Samples Realm", + "notBefore": 0, + "revokeRefreshToken": false, + "refreshTokenMaxReuse": 0, + "accessTokenLifespan": 300, + "accessTokenLifespanForImplicitFlow": 900, + "ssoSessionIdleTimeout": 1800, + "ssoSessionMaxLifespan": 36000, + "ssoSessionIdleTimeoutRememberMe": 0, + "ssoSessionMaxLifespanRememberMe": 0, + "offlineSessionIdleTimeout": 2592000, + "offlineSessionMaxLifespanEnabled": false, + "offlineSessionMaxLifespan": 5184000, + "clientSessionIdleTimeout": 0, + "clientSessionMaxLifespan": 0, + "clientOfflineSessionIdleTimeout": 0, + "clientOfflineSessionMaxLifespan": 0, + "accessCodeLifespan": 60, + "accessCodeLifespanUserAction": 300, + "accessCodeLifespanLogin": 1800, + "actionTokenGeneratedByAdminLifespan": 43200, + "actionTokenGeneratedByUserLifespan": 300, + "enabled": true, + "sslRequired": "external", + "registrationAllowed": false, + "registrationEmailAsUsername": false, + "rememberMe": false, + "verifyEmail": false, + "loginWithEmailAllowed": true, + "duplicateEmailsAllowed": false, + "resetPasswordAllowed": false, + "editUsernameAllowed": false, + "bruteForceProtected": false, + "permanentLockout": false, + "maxFailureWaitSeconds": 900, + "minimumQuickLoginWaitSeconds": 60, + "waitIncrementSeconds": 60, + "quickLoginCheckMilliSeconds": 1000, + "maxDeltaTimeSeconds": 43200, + "failureFactor": 30, + "roles": { + "realm": [ + { + "id": "76e45e9c-da88-45a0-9add-d4d6a5706098", + "name": "pkceUser", + "description": "Role sample for PKCE", + "composite": false, + "clientRole": false, + "containerId": "SeedSamplesRealm", + "attributes": {} + }, + { + "id": "1b7e58cc-b1b3-43e5-ba55-6b66d2cc9efd", + "name": "offline_access", + "description": "${role_offline-access}", + "composite": false, + "clientRole": false, + "containerId": "SeedSamplesRealm", + "attributes": {} + }, + { + "id": "277945e4-b40d-4486-845e-ea53967efa9b", + "name": "uma_authorization", + "description": "${role_uma_authorization}", + "composite": false, + "clientRole": false, + "containerId": "SeedSamplesRealm", + "attributes": {} + } + ], + "client": { + "SeedStackClientCodeFlow": [ + { + "id": "85845c8b-675d-4830-8b64-f3a609548a5f", + "name": "profile", + "composite": false, + "clientRole": true, + "containerId": "111dfb66-de7a-4950-9d59-b0b4332f8f85", + "attributes": {} + } + ], + "SeedStackClientCredentials": [], + "realm-management": [ + { + "id": "f631d1fb-9d7b-4343-9e26-f46df618bed2", + "name": "view-users", + "description": "${role_view-users}", + "composite": true, + "composites": { + "client": { + "realm-management": [ + "query-users", + "query-groups" + ] + } + }, + "clientRole": true, + "containerId": "c0498ab1-060f-4002-ac9f-fcac62e29df3", + "attributes": {} + }, + { + "id": "70d4f087-7177-46ff-ba03-738b6280ef4c", + "name": "impersonation", + "description": "${role_impersonation}", + "composite": false, + "clientRole": true, + "containerId": "c0498ab1-060f-4002-ac9f-fcac62e29df3", + "attributes": {} + }, + { + "id": "0ca3e4c4-93b5-4f07-8a41-df43e481d8cd", + "name": "view-events", + "description": "${role_view-events}", + "composite": false, + "clientRole": true, + "containerId": "c0498ab1-060f-4002-ac9f-fcac62e29df3", + "attributes": {} + }, + { + "id": "95ca3ac1-c190-4998-8e47-c328967e39c6", + "name": "manage-authorization", + "description": "${role_manage-authorization}", + "composite": false, + "clientRole": true, + "containerId": "c0498ab1-060f-4002-ac9f-fcac62e29df3", + "attributes": {} + }, + { + "id": "de067413-f98c-4a19-b028-0f9d4b23032b", + "name": "manage-events", + "description": "${role_manage-events}", + "composite": false, + "clientRole": true, + "containerId": "c0498ab1-060f-4002-ac9f-fcac62e29df3", + "attributes": {} + }, + { + "id": "9416481d-8d54-478e-b980-c490ef86c91c", + "name": "query-clients", + "description": "${role_query-clients}", + "composite": false, + "clientRole": true, + "containerId": "c0498ab1-060f-4002-ac9f-fcac62e29df3", + "attributes": {} + }, + { + "id": "0ec8f494-b7e9-4281-9bc0-c61cbc79ee49", + "name": "view-identity-providers", + "description": "${role_view-identity-providers}", + "composite": false, + "clientRole": true, + "containerId": "c0498ab1-060f-4002-ac9f-fcac62e29df3", + "attributes": {} + }, + { + "id": "af931001-e222-4e50-84c1-5c348ab7928f", + "name": "view-realm", + "description": "${role_view-realm}", + "composite": false, + "clientRole": true, + "containerId": "c0498ab1-060f-4002-ac9f-fcac62e29df3", + "attributes": {} + }, + { + "id": "72248c68-937b-4feb-91e8-edabd3de2403", + "name": "realm-admin", + "description": "${role_realm-admin}", + "composite": true, + "composites": { + "client": { + "realm-management": [ + "impersonation", + "view-users", + "view-events", + "manage-authorization", + "query-clients", + "manage-events", + "view-identity-providers", + "view-realm", + "query-groups", + "query-realms", + "view-clients", + "manage-users", + "query-users", + "create-client", + "manage-realm", + "manage-identity-providers", + "view-authorization", + "manage-clients" + ] + } + }, + "clientRole": true, + "containerId": "c0498ab1-060f-4002-ac9f-fcac62e29df3", + "attributes": {} + }, + { + "id": "21be3081-e6db-4f2f-9a94-15d184571e2c", + "name": "query-groups", + "description": "${role_query-groups}", + "composite": false, + "clientRole": true, + "containerId": "c0498ab1-060f-4002-ac9f-fcac62e29df3", + "attributes": {} + }, + { + "id": "54cdfc1d-b264-406e-8d6f-fbc37cbc07f0", + "name": "query-realms", + "description": "${role_query-realms}", + "composite": false, + "clientRole": true, + "containerId": "c0498ab1-060f-4002-ac9f-fcac62e29df3", + "attributes": {} + }, + { + "id": "93f6973c-f06a-4008-9d90-1856bc69730f", + "name": "view-clients", + "description": "${role_view-clients}", + "composite": true, + "composites": { + "client": { + "realm-management": [ + "query-clients" + ] + } + }, + "clientRole": true, + "containerId": "c0498ab1-060f-4002-ac9f-fcac62e29df3", + "attributes": {} + }, + { + "id": "5e0cf02f-2edc-4198-b5cc-2a56482b4aba", + "name": "manage-users", + "description": "${role_manage-users}", + "composite": false, + "clientRole": true, + "containerId": "c0498ab1-060f-4002-ac9f-fcac62e29df3", + "attributes": {} + }, + { + "id": "ade7fff8-d11e-4ee7-a0f5-3d809aefa0f2", + "name": "create-client", + "description": "${role_create-client}", + "composite": false, + "clientRole": true, + "containerId": "c0498ab1-060f-4002-ac9f-fcac62e29df3", + "attributes": {} + }, + { + "id": "490be407-fc8d-4dc0-814c-21fe95ed3630", + "name": "query-users", + "description": "${role_query-users}", + "composite": false, + "clientRole": true, + "containerId": "c0498ab1-060f-4002-ac9f-fcac62e29df3", + "attributes": {} + }, + { + "id": "6b570153-978d-4756-8e1a-12bf7cf605d5", + "name": "manage-realm", + "description": "${role_manage-realm}", + "composite": false, + "clientRole": true, + "containerId": "c0498ab1-060f-4002-ac9f-fcac62e29df3", + "attributes": {} + }, + { + "id": "c45c42bb-2a63-42af-8bb5-6838cc6b1a64", + "name": "manage-identity-providers", + "description": "${role_manage-identity-providers}", + "composite": false, + "clientRole": true, + "containerId": "c0498ab1-060f-4002-ac9f-fcac62e29df3", + "attributes": {} + }, + { + "id": "274a68ab-dc84-44ef-b5ce-f51b91ebe7d1", + "name": "view-authorization", + "description": "${role_view-authorization}", + "composite": false, + "clientRole": true, + "containerId": "c0498ab1-060f-4002-ac9f-fcac62e29df3", + "attributes": {} + }, + { + "id": "c6a82fa9-dfae-4884-9c25-84066ee2cb07", + "name": "manage-clients", + "description": "${role_manage-clients}", + "composite": false, + "clientRole": true, + "containerId": "c0498ab1-060f-4002-ac9f-fcac62e29df3", + "attributes": {} + } + ], + "security-admin-console": [], + "admin-cli": [], + "account-console": [], + "broker": [ + { + "id": "01a32c8f-0ec3-4482-bd7f-b79b6c78894d", + "name": "read-token", + "description": "${role_read-token}", + "composite": false, + "clientRole": true, + "containerId": "9b7d4f17-5986-44f9-874b-b0b84a92a255", + "attributes": {} + } + ], + "SeedStackClientCodePkce": [], + "account": [ + { + "id": "d67a0b39-3114-4b5a-8894-13750ce030b6", + "name": "manage-account-links", + "description": "${role_manage-account-links}", + "composite": false, + "clientRole": true, + "containerId": "a43055a0-c78b-4482-9e47-a54232964c3c", + "attributes": {} + }, + { + "id": "47e4ae6f-5ce7-47cc-b013-3651380ab752", + "name": "manage-account", + "description": "${role_manage-account}", + "composite": true, + "composites": { + "client": { + "account": [ + "manage-account-links" + ] + } + }, + "clientRole": true, + "containerId": "a43055a0-c78b-4482-9e47-a54232964c3c", + "attributes": {} + }, + { + "id": "235542d5-0b91-4490-8b0f-381d5ff6005d", + "name": "view-profile", + "description": "${role_view-profile}", + "composite": false, + "clientRole": true, + "containerId": "a43055a0-c78b-4482-9e47-a54232964c3c", + "attributes": {} + }, + { + "id": "3cdfa407-3ae2-4ef3-9f2f-d414845e23a3", + "name": "view-consent", + "description": "${role_view-consent}", + "composite": false, + "clientRole": true, + "containerId": "a43055a0-c78b-4482-9e47-a54232964c3c", + "attributes": {} + }, + { + "id": "c26193c0-1367-49ed-99da-25db748b5c34", + "name": "view-applications", + "description": "${role_view-applications}", + "composite": false, + "clientRole": true, + "containerId": "a43055a0-c78b-4482-9e47-a54232964c3c", + "attributes": {} + }, + { + "id": "ba315e1b-3e81-4696-8224-938560c623a4", + "name": "manage-consent", + "description": "${role_manage-consent}", + "composite": true, + "composites": { + "client": { + "account": [ + "view-consent" + ] + } + }, + "clientRole": true, + "containerId": "a43055a0-c78b-4482-9e47-a54232964c3c", + "attributes": {} + } + ] + } + }, + "groups": [], + "defaultRoles": [ + "offline_access", + "uma_authorization" + ], + "requiredCredentials": [ + "password" + ], + "otpPolicyType": "totp", + "otpPolicyAlgorithm": "HmacSHA1", + "otpPolicyInitialCounter": 0, + "otpPolicyDigits": 6, + "otpPolicyLookAheadWindow": 1, + "otpPolicyPeriod": 30, + "otpSupportedApplications": [ + "FreeOTP", + "Google Authenticator" + ], + "webAuthnPolicyRpEntityName": "keycloak", + "webAuthnPolicySignatureAlgorithms": [ + "ES256" + ], + "webAuthnPolicyRpId": "", + "webAuthnPolicyAttestationConveyancePreference": "not specified", + "webAuthnPolicyAuthenticatorAttachment": "not specified", + "webAuthnPolicyRequireResidentKey": "not specified", + "webAuthnPolicyUserVerificationRequirement": "not specified", + "webAuthnPolicyCreateTimeout": 0, + "webAuthnPolicyAvoidSameAuthenticatorRegister": false, + "webAuthnPolicyAcceptableAaguids": [], + "webAuthnPolicyPasswordlessRpEntityName": "keycloak", + "webAuthnPolicyPasswordlessSignatureAlgorithms": [ + "ES256" + ], + "webAuthnPolicyPasswordlessRpId": "", + "webAuthnPolicyPasswordlessAttestationConveyancePreference": "not specified", + "webAuthnPolicyPasswordlessAuthenticatorAttachment": "not specified", + "webAuthnPolicyPasswordlessRequireResidentKey": "not specified", + "webAuthnPolicyPasswordlessUserVerificationRequirement": "not specified", + "webAuthnPolicyPasswordlessCreateTimeout": 0, + "webAuthnPolicyPasswordlessAvoidSameAuthenticatorRegister": false, + "webAuthnPolicyPasswordlessAcceptableAaguids": [], + "users": [ + { + "id": "2f5ccae2-479f-418f-92e9-5b3fde1d23fd", + "createdTimestamp": 1605278087827, + "username": "jane smith", + "enabled": true, + "totp": false, + "emailVerified": false, + "firstName": "Jane", + "lastName": "Smith", + "email": "jane.smith@fakemail.com", + "credentials": [ + { + "id": "5f9ebcbc-c9cb-4748-9426-55b95f353833", + "type": "password", + "createdDate": 1605278118094, + "secretData": "{\"value\":\"NOrXQ02bkQwizZ/4fM+k8Mq3KLKU80gq1ax+z0kDOruBSwufa1Y64BvKfxzeSerOsztIU5QleP8EITdXYyeQQQ==\",\"salt\":\"DLF5s/kW14XVwqsOkXQF3g==\"}", + "credentialData": "{\"hashIterations\":27500,\"algorithm\":\"pbkdf2-sha256\"}" + } + ], + "disableableCredentialTypes": [], + "requiredActions": [], + "realmRoles": [ + "offline_access", + "uma_authorization" + ], + "clientRoles": { + "account": [ + "manage-account", + "view-profile" + ] + }, + "notBefore": 0, + "groups": [] + }, + { + "id": "dc99d3d6-8eb9-4bce-b000-cd10197e6d00", + "createdTimestamp": 1603370904238, + "username": "john doe", + "enabled": true, + "totp": false, + "emailVerified": false, + "firstName": "John", + "lastName": "Doe", + "email": "john.doe@fakemail.com", + "attributes": { + "picture": [ + "http://localhost:8090/mascot-happy.png" + ] + }, + "credentials": [ + { + "id": "36e9e620-40c2-4564-bd97-18d537f1f8b2", + "type": "password", + "createdDate": 1603461934205, + "secretData": "{\"value\":\"x5DYSxVEaf8g5Qe6W5w8McyrDicj3kh/GUZpXPNKXiUGBvy5nPZJSKyKZGYIRqiZs3TwreQuzDGIXD5n3nbB1w==\",\"salt\":\"47qfRW/dohy764JpjkkJ/g==\"}", + "credentialData": "{\"hashIterations\":27500,\"algorithm\":\"pbkdf2-sha256\"}" + } + ], + "disableableCredentialTypes": [], + "requiredActions": [], + "realmRoles": [ + "pkceUser", + "offline_access", + "uma_authorization" + ], + "clientRoles": { + "account": [ + "manage-account", + "view-profile" + ] + }, + "clientConsents": [ + { + "clientId": "SeedStackClientCodeFlow", + "grantedClientScopes": [ + "CodeFlowScope", + "profile", + "roles", + "email" + ], + "createdDate": 1603719078966, + "lastUpdatedDate": 1605278011512 + } + ], + "notBefore": 0, + "groups": [] + }, + { + "id": "0a6e95f2-5d65-40cd-b974-b46364dc3dc6", + "createdTimestamp": 1604323826439, + "username": "service-account-seedstackclientcredentials", + "enabled": true, + "totp": false, + "emailVerified": false, + "serviceAccountClientId": "SeedStackClientCredentials", + "credentials": [], + "disableableCredentialTypes": [], + "requiredActions": [], + "realmRoles": [ + "offline_access", + "uma_authorization" + ], + "clientRoles": { + "account": [ + "manage-account", + "view-profile" + ] + }, + "notBefore": 0, + "groups": [] + } + ], + "scopeMappings": [ + { + "clientScope": "offline_access", + "roles": [ + "offline_access" + ] + }, + { + "clientScope": "pkceScope", + "roles": [ + "pkceUser" + ] + } + ], + "clientScopeMappings": { + "account": [ + { + "client": "SeedStackClientCredentials", + "roles": [ + "manage-account", + "view-applications", + "view-consent", + "manage-account-links", + "manage-consent", + "view-profile" + ] + }, + { + "client": "account-console", + "roles": [ + "manage-account" + ] + } + ] + }, + "clients": [ + { + "id": "111dfb66-de7a-4950-9d59-b0b4332f8f85", + "clientId": "SeedStackClientCodeFlow", + "name": "SeedStackClientCodeFlow", + "description": "Client for sample Authorization code flow", + "rootUrl": "http://localhost:8090", + "adminUrl": "http://localhost:8090", + "baseUrl": "", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "secret": "3545c109-9cf0-4ccf-82aa-89464f798844", + "redirectUris": [ + "http://localhost:8090/callback" + ], + "webOrigins": [ + "http://localhost:8090" + ], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": true, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": true, + "serviceAccountsEnabled": false, + "publicClient": false, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": { + "saml.assertion.signature": "false", + "saml.force.post.binding": "false", + "saml.multivalued.roles": "false", + "saml.encrypt": "false", + "saml.server.signature": "false", + "saml.server.signature.keyinfo.ext": "false", + "exclude.session.state.from.auth.response": "false", + "saml_force_name_id_format": "false", + "saml.client.signature": "false", + "tls.client.certificate.bound.access.tokens": "false", + "saml.authnstatement": "false", + "display.on.consent.screen": "false", + "saml.onetimeuse.condition": "false" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": true, + "nodeReRegistrationTimeout": -1, + "defaultClientScopes": [ + "web-origins", + "role_list", + "profile", + "roles", + "email" + ], + "optionalClientScopes": [ + "address", + "CodeFlowScope", + "phone", + "offline_access", + "microprofile-jwt" + ] + }, + { + "id": "16f23c71-49bb-427f-b1b3-72f3b1bf8c4c", + "clientId": "SeedStackClientCodePkce", + "name": "SeedStackClientCodePkce", + "description": "Auth server client for PKCE sample", + "rootUrl": "http://localhost:8090", + "adminUrl": "http://localhost:8090", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "secret": "e49e4a6f-c7b1-4a5d-b26d-e6a03278dc4d", + "redirectUris": [ + "http://localhost:8090/*" + ], + "webOrigins": [ + "http://localhost:8090" + ], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": true, + "serviceAccountsEnabled": false, + "publicClient": true, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": { + "saml.assertion.signature": "false", + "saml.force.post.binding": "false", + "saml.multivalued.roles": "false", + "saml.encrypt": "false", + "saml.server.signature": "false", + "saml.server.signature.keyinfo.ext": "false", + "exclude.session.state.from.auth.response": "false", + "saml_force_name_id_format": "false", + "saml.client.signature": "false", + "tls.client.certificate.bound.access.tokens": "false", + "saml.authnstatement": "false", + "display.on.consent.screen": "false", + "pkce.code.challenge.method": "S256", + "saml.onetimeuse.condition": "false" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": true, + "nodeReRegistrationTimeout": -1, + "defaultClientScopes": [ + "web-origins", + "role_list", + "profile", + "roles", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "pkceScope", + "offline_access", + "microprofile-jwt" + ] + }, + { + "id": "c5dfd3c7-1852-4f62-bfa1-caa22a9eadf0", + "clientId": "SeedStackClientCredentials", + "name": "SeedStackClientCredentials", + "description": "OAuth samples - Client credentials flow", + "rootUrl": "http://localhost:8090", + "adminUrl": "http://localhost:8090", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "secret": "8b8d20f8-281d-41b4-84ef-be9418b4a21c", + "redirectUris": [ + "http://localhost:8090/*" + ], + "webOrigins": [ + "http://localhost:8090" + ], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": false, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": true, + "publicClient": false, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": { + "saml.assertion.signature": "false", + "saml.force.post.binding": "false", + "saml.multivalued.roles": "false", + "saml.encrypt": "false", + "saml.server.signature": "false", + "saml.server.signature.keyinfo.ext": "false", + "exclude.session.state.from.auth.response": "false", + "saml_force_name_id_format": "false", + "saml.client.signature": "false", + "tls.client.certificate.bound.access.tokens": "false", + "saml.authnstatement": "false", + "display.on.consent.screen": "false", + "saml.onetimeuse.condition": "false" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": -1, + "protocolMappers": [ + { + "id": "6fa32c60-a7a2-4992-9774-2a5433fcacfe", + "name": "Client ID", + "protocol": "openid-connect", + "protocolMapper": "oidc-usersessionmodel-note-mapper", + "consentRequired": false, + "config": { + "user.session.note": "clientId", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "clientId", + "jsonType.label": "String" + } + }, + { + "id": "faeaebff-693c-4b16-a7ed-2198d1ce2fc1", + "name": "Client Host", + "protocol": "openid-connect", + "protocolMapper": "oidc-usersessionmodel-note-mapper", + "consentRequired": false, + "config": { + "user.session.note": "clientHost", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "clientHost", + "jsonType.label": "String" + } + }, + { + "id": "837642fa-b2d0-469e-97fa-4c725cab9107", + "name": "Client IP Address", + "protocol": "openid-connect", + "protocolMapper": "oidc-usersessionmodel-note-mapper", + "consentRequired": false, + "config": { + "user.session.note": "clientAddress", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "clientAddress", + "jsonType.label": "String" + } + } + ], + "defaultClientScopes": [ + "web-origins", + "role_list", + "profile", + "roles", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ] + }, + { + "id": "a43055a0-c78b-4482-9e47-a54232964c3c", + "clientId": "account", + "name": "${client_account}", + "rootUrl": "${authBaseUrl}", + "baseUrl": "/realms/SeedSamplesRealm/account/", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "secret": "6100802e-506a-4ad5-813d-1ccf1148d25c", + "defaultRoles": [ + "view-profile", + "manage-account" + ], + "redirectUris": [ + "/realms/SeedSamplesRealm/account/*" + ], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "publicClient": false, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": {}, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "defaultClientScopes": [ + "web-origins", + "role_list", + "profile", + "roles", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ] + }, + { + "id": "8d85de7a-0203-495c-8c2f-099cc7013237", + "clientId": "account-console", + "name": "${client_account-console}", + "rootUrl": "${authBaseUrl}", + "baseUrl": "/realms/SeedSamplesRealm/account/", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "secret": "a89eada0-7cea-475d-b01e-791915133b54", + "redirectUris": [ + "/realms/SeedSamplesRealm/account/*" + ], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "publicClient": true, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": { + "pkce.code.challenge.method": "S256" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "protocolMappers": [ + { + "id": "37d938d5-1ffe-408b-b189-89d767dd0f71", + "name": "audience resolve", + "protocol": "openid-connect", + "protocolMapper": "oidc-audience-resolve-mapper", + "consentRequired": false, + "config": {} + } + ], + "defaultClientScopes": [ + "web-origins", + "role_list", + "profile", + "roles", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ] + }, + { + "id": "c1d849ea-0234-41fc-a256-379d3fe28ccf", + "clientId": "admin-cli", + "name": "${client_admin-cli}", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "secret": "c1cfd6df-f659-4465-aa83-811a752643aa", + "redirectUris": [], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": false, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": true, + "serviceAccountsEnabled": false, + "publicClient": true, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": {}, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "defaultClientScopes": [ + "web-origins", + "role_list", + "profile", + "roles", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ] + }, + { + "id": "9b7d4f17-5986-44f9-874b-b0b84a92a255", + "clientId": "broker", + "name": "${client_broker}", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "secret": "4e0fe54b-2285-4389-9a47-b2d1e8e5c235", + "redirectUris": [], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "publicClient": false, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": {}, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "defaultClientScopes": [ + "web-origins", + "role_list", + "profile", + "roles", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ] + }, + { + "id": "c0498ab1-060f-4002-ac9f-fcac62e29df3", + "clientId": "realm-management", + "name": "${client_realm-management}", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "secret": "87d0ff3a-6b00-48aa-b50e-2119f08a686e", + "redirectUris": [], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": true, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "publicClient": false, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": {}, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "defaultClientScopes": [ + "web-origins", + "role_list", + "profile", + "roles", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ] + }, + { + "id": "2c11d876-a78b-4ebf-8175-17b0fdc90ac6", + "clientId": "security-admin-console", + "name": "${client_security-admin-console}", + "rootUrl": "${authAdminUrl}", + "baseUrl": "/admin/SeedSamplesRealm/console/", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "secret": "2a35cd4a-43da-41d2-80a1-d8a97801b91e", + "redirectUris": [ + "/admin/SeedSamplesRealm/console/*" + ], + "webOrigins": [ + "+" + ], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "publicClient": true, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": { + "pkce.code.challenge.method": "S256" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "protocolMappers": [ + { + "id": "c8c59220-355b-4015-a729-7c4cde55ebb5", + "name": "locale", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "locale", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "locale", + "jsonType.label": "String" + } + } + ], + "defaultClientScopes": [ + "web-origins", + "role_list", + "profile", + "roles", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ] + } + ], + "clientScopes": [ + { + "id": "7473fe1b-0c3b-4341-bf97-cab4edd4bce2", + "name": "CodeFlowScope", + "description": "Scope that only John Doe agreed", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "display.on.consent.screen": "true", + "consent.screen.text": "CodeFlowScope sample conssent" + } + }, + { + "id": "206367e5-ee54-483d-9d39-af5bf1fe9faf", + "name": "address", + "description": "OpenID Connect built-in scope: address", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "display.on.consent.screen": "true", + "consent.screen.text": "${addressScopeConsentText}" + }, + "protocolMappers": [ + { + "id": "4a27dd5c-58d7-442f-be42-a40a24b5ecb8", + "name": "address", + "protocol": "openid-connect", + "protocolMapper": "oidc-address-mapper", + "consentRequired": false, + "config": { + "user.attribute.formatted": "formatted", + "user.attribute.country": "country", + "user.attribute.postal_code": "postal_code", + "userinfo.token.claim": "true", + "user.attribute.street": "street", + "id.token.claim": "true", + "user.attribute.region": "region", + "access.token.claim": "true", + "user.attribute.locality": "locality" + } + } + ] + }, + { + "id": "ce8b4a72-4a92-440c-bfb9-cc10498f63a8", + "name": "email", + "description": "OpenID Connect built-in scope: email", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "display.on.consent.screen": "true", + "consent.screen.text": "${emailScopeConsentText}" + }, + "protocolMappers": [ + { + "id": "3d062777-d221-47c6-8296-5126c16a9cc7", + "name": "email verified", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "emailVerified", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "email_verified", + "jsonType.label": "boolean" + } + }, + { + "id": "35cbb15f-fc1e-4e18-bbf4-ab0299116456", + "name": "email", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "email", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "email", + "jsonType.label": "String" + } + } + ] + }, + { + "id": "1f1804a9-64ad-4446-b9e1-c9667decbfc7", + "name": "microprofile-jwt", + "description": "Microprofile - JWT built-in scope", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "display.on.consent.screen": "false" + }, + "protocolMappers": [ + { + "id": "c66d7a4e-5ec9-491e-a8c5-d5742effcc58", + "name": "upn", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "username", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "upn", + "jsonType.label": "String" + } + }, + { + "id": "fc6f5acf-52d8-4dc7-8ac6-95e155ea28d0", + "name": "groups", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-realm-role-mapper", + "consentRequired": false, + "config": { + "multivalued": "true", + "user.attribute": "foo", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "groups", + "jsonType.label": "String" + } + } + ] + }, + { + "id": "492a7157-f6a5-4135-bc5d-aacbbe747abc", + "name": "offline_access", + "description": "OpenID Connect built-in scope: offline_access", + "protocol": "openid-connect", + "attributes": { + "consent.screen.text": "${offlineAccessScopeConsentText}", + "display.on.consent.screen": "true" + } + }, + { + "id": "df7f44f1-6203-455b-94e9-1a24adb547f2", + "name": "phone", + "description": "OpenID Connect built-in scope: phone", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "display.on.consent.screen": "true", + "consent.screen.text": "${phoneScopeConsentText}" + }, + "protocolMappers": [ + { + "id": "206ff088-4c64-4b7f-b33b-495d3f5f81a7", + "name": "phone number", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "phoneNumber", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "phone_number", + "jsonType.label": "String" + } + }, + { + "id": "dc14638b-d896-466f-904e-7003704ed777", + "name": "phone number verified", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "phoneNumberVerified", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "phone_number_verified", + "jsonType.label": "boolean" + } + } + ] + }, + { + "id": "e5969808-5a08-440f-97bf-88880303e1bf", + "name": "pkceScope", + "description": "Sample scope for Pkce", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "display.on.consent.screen": "false" + } + }, + { + "id": "723c8d2d-22c6-435e-8e5a-c9b3fffccfac", + "name": "profile", + "description": "OpenID Connect built-in scope: profile", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "display.on.consent.screen": "true", + "consent.screen.text": "${profileScopeConsentText}" + }, + "protocolMappers": [ + { + "id": "60c7189f-2300-47a3-a03c-fad59287fefd", + "name": "website", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "website", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "website", + "jsonType.label": "String" + } + }, + { + "id": "149a6e34-33ce-41f0-8bf2-9aca5bd23ea9", + "name": "nickname", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "nickname", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "nickname", + "jsonType.label": "String" + } + }, + { + "id": "79ec3c7b-8cf8-4ca7-8a60-57d20364fdd9", + "name": "profile", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "profile", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "profile", + "jsonType.label": "String" + } + }, + { + "id": "2f71f8ed-59be-4b4a-bc7a-86a4f8e64836", + "name": "updated at", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "updatedAt", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "updated_at", + "jsonType.label": "String" + } + }, + { + "id": "a4128248-5135-4768-a573-d3bd556472cc", + "name": "middle name", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "middleName", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "middle_name", + "jsonType.label": "String" + } + }, + { + "id": "76580c63-91aa-4cd0-81f9-f36e77683e3c", + "name": "birthdate", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "birthdate", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "birthdate", + "jsonType.label": "String" + } + }, + { + "id": "a5cfc941-2254-45a2-a175-b4f20d67083f", + "name": "locale", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "locale", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "locale", + "jsonType.label": "String" + } + }, + { + "id": "00ae402c-174f-4bd5-8347-c7a795d9c1c7", + "name": "username", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "username", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "preferred_username", + "jsonType.label": "String" + } + }, + { + "id": "3c0a0f01-5bb9-4a07-a1a5-8b12f52dff7c", + "name": "gender", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "gender", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "gender", + "jsonType.label": "String" + } + }, + { + "id": "f5830ab3-2d7c-4bc5-910c-ada614b34be2", + "name": "picture", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "picture", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "picture", + "jsonType.label": "String" + } + }, + { + "id": "01026e89-bf73-4c93-93d8-b04ea6b0ea89", + "name": "zoneinfo", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "zoneinfo", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "zoneinfo", + "jsonType.label": "String" + } + }, + { + "id": "11542b0f-1ff0-4836-8bf6-3e0317e2f31f", + "name": "full name", + "protocol": "openid-connect", + "protocolMapper": "oidc-full-name-mapper", + "consentRequired": false, + "config": { + "id.token.claim": "true", + "access.token.claim": "true", + "userinfo.token.claim": "true" + } + }, + { + "id": "ccf07728-d17b-4ace-a88f-850c4f4c2163", + "name": "given name", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "firstName", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "given_name", + "jsonType.label": "String" + } + }, + { + "id": "0f74817e-aabf-4eee-9aba-02719066796a", + "name": "family name", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "lastName", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "family_name", + "jsonType.label": "String" + } + } + ] + }, + { + "id": "b52dd45e-8119-4af6-8635-1abd9bf365dd", + "name": "role_list", + "description": "SAML role list", + "protocol": "saml", + "attributes": { + "consent.screen.text": "${samlRoleListScopeConsentText}", + "display.on.consent.screen": "true" + }, + "protocolMappers": [ + { + "id": "e8061a4f-b4c5-44f2-96a3-af49942c8244", + "name": "role list", + "protocol": "saml", + "protocolMapper": "saml-role-list-mapper", + "consentRequired": false, + "config": { + "single": "false", + "attribute.nameformat": "Basic", + "attribute.name": "Role" + } + } + ] + }, + { + "id": "a249e0a3-9d01-4998-9525-cc8f7a89432c", + "name": "roles", + "description": "OpenID Connect scope for add user roles to the access token", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "false", + "display.on.consent.screen": "true", + "consent.screen.text": "${rolesScopeConsentText}" + }, + "protocolMappers": [ + { + "id": "6dde4fc9-733a-4d71-9c4f-61aeb80c5bc6", + "name": "client roles", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-client-role-mapper", + "consentRequired": false, + "config": { + "user.attribute": "foo", + "access.token.claim": "true", + "claim.name": "resource_access.${client_id}.roles", + "jsonType.label": "String", + "multivalued": "true" + } + }, + { + "id": "5b2dc6ce-3c3f-4721-bfd5-9891601f9de4", + "name": "audience resolve", + "protocol": "openid-connect", + "protocolMapper": "oidc-audience-resolve-mapper", + "consentRequired": false, + "config": {} + }, + { + "id": "19b2529a-453a-4562-8f8d-e1a48d11e7ed", + "name": "realm roles", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-realm-role-mapper", + "consentRequired": false, + "config": { + "user.attribute": "foo", + "access.token.claim": "true", + "claim.name": "realm_access.roles", + "jsonType.label": "String", + "multivalued": "true" + } + } + ] + }, + { + "id": "bd5db82d-bc43-4c3c-b51a-1db573e9d748", + "name": "web-origins", + "description": "OpenID Connect scope for add allowed web origins to the access token", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "false", + "display.on.consent.screen": "false", + "consent.screen.text": "" + }, + "protocolMappers": [ + { + "id": "91523e5d-cb47-484d-8f31-631a78ca2741", + "name": "allowed web origins", + "protocol": "openid-connect", + "protocolMapper": "oidc-allowed-origins-mapper", + "consentRequired": false, + "config": {} + } + ] + } + ], + "defaultDefaultClientScopes": [ + "role_list", + "profile", + "email", + "roles", + "web-origins" + ], + "defaultOptionalClientScopes": [ + "offline_access", + "address", + "phone", + "microprofile-jwt" + ], + "browserSecurityHeaders": { + "contentSecurityPolicyReportOnly": "", + "xContentTypeOptions": "nosniff", + "xRobotsTag": "none", + "xFrameOptions": "SAMEORIGIN", + "contentSecurityPolicy": "frame-src 'self'; frame-ancestors 'self'; object-src 'none';", + "xXSSProtection": "1; mode=block", + "strictTransportSecurity": "max-age=31536000; includeSubDomains" + }, + "smtpServer": {}, + "eventsEnabled": false, + "eventsListeners": [ + "jboss-logging" + ], + "enabledEventTypes": [], + "adminEventsEnabled": false, + "adminEventsDetailsEnabled": false, + "components": { + "org.keycloak.services.clientregistration.policy.ClientRegistrationPolicy": [ + { + "id": "1e48261f-84de-4228-bf37-6c61947ad358", + "name": "Allowed Protocol Mapper Types", + "providerId": "allowed-protocol-mappers", + "subType": "anonymous", + "subComponents": {}, + "config": { + "allowed-protocol-mapper-types": [ + "oidc-sha256-pairwise-sub-mapper", + "oidc-usermodel-property-mapper", + "oidc-address-mapper", + "saml-role-list-mapper", + "saml-user-attribute-mapper", + "oidc-usermodel-attribute-mapper", + "saml-user-property-mapper", + "oidc-full-name-mapper" + ] + } + }, + { + "id": "b0cb967d-fae9-40c6-b98e-fe01ed8ecaed", + "name": "Full Scope Disabled", + "providerId": "scope", + "subType": "anonymous", + "subComponents": {}, + "config": {} + }, + { + "id": "e5100633-abac-4e64-bb1b-2196767b82af", + "name": "Allowed Client Scopes", + "providerId": "allowed-client-templates", + "subType": "authenticated", + "subComponents": {}, + "config": { + "allow-default-scopes": [ + "true" + ] + } + }, + { + "id": "2abd8f23-ee43-4c3a-8489-016e9add0693", + "name": "Max Clients Limit", + "providerId": "max-clients", + "subType": "anonymous", + "subComponents": {}, + "config": { + "max-clients": [ + "200" + ] + } + }, + { + "id": "bc2db5d1-4dc3-419f-b154-fc2a8399ee43", + "name": "Allowed Protocol Mapper Types", + "providerId": "allowed-protocol-mappers", + "subType": "authenticated", + "subComponents": {}, + "config": { + "allowed-protocol-mapper-types": [ + "saml-user-attribute-mapper", + "oidc-usermodel-property-mapper", + "saml-user-property-mapper", + "oidc-usermodel-attribute-mapper", + "oidc-full-name-mapper", + "oidc-sha256-pairwise-sub-mapper", + "saml-role-list-mapper", + "oidc-address-mapper" + ] + } + }, + { + "id": "620080bc-e3e4-4260-8c94-3eb6bc76ea62", + "name": "Trusted Hosts", + "providerId": "trusted-hosts", + "subType": "anonymous", + "subComponents": {}, + "config": { + "host-sending-registration-request-must-match": [ + "true" + ], + "client-uris-must-match": [ + "true" + ] + } + }, + { + "id": "97759785-0cf6-4a4e-bdb7-9a860f5c540a", + "name": "Consent Required", + "providerId": "consent-required", + "subType": "anonymous", + "subComponents": {}, + "config": {} + }, + { + "id": "956a9ed5-f07e-40da-8674-2a3180ffb51b", + "name": "Allowed Client Scopes", + "providerId": "allowed-client-templates", + "subType": "anonymous", + "subComponents": {}, + "config": { + "allow-default-scopes": [ + "true" + ] + } + } + ], + "org.keycloak.keys.KeyProvider": [ + { + "id": "385f29b9-66d0-43b5-a630-ff7d653ef0aa", + "name": "hmac-generated", + "providerId": "hmac-generated", + "subComponents": {}, + "config": { + "kid": [ + "7318790d-a0c4-4db5-9452-2633d841b23b" + ], + "secret": [ + "AYMP6wdffnp4L3ZUbsA0IZFF1rOeaX3AsNP_9jsb3ROPYZy2IgJVbBDqGINzpA3Yi-G4HgtrPBIDQvLd5OVzNg" + ], + "priority": [ + "100" + ], + "algorithm": [ + "HS256" + ] + } + }, + { + "id": "be65c954-58f8-4977-a69f-6f23ec899a61", + "name": "rsa-generated", + "providerId": "rsa-generated", + "subComponents": {}, + "config": { + "privateKey": [ + "MIIEowIBAAKCAQEAxXogZjUQ+pP0YIwg6g2jfAHMo4S4PGqbwZ4FUxRmGVsmEG7sbTEd/SG4lN7AQfQaTpFXAQbAPlk+/xAcqT438nPLpIn25VIW7ByqUIiKVzpoMbcyYK7gN1szlwc+pDxEC/MfWv0lAnPgVJNbWHMEwkhSiosGNz4vE78JZvfAoZFKUmZY6doRy47bI5VfFAcocoR4d7cSODCQfWR2Puh19y1ISZexw/nkQ6kdCnEwy0LjuN7SedX2wzK9MJeR/yp1eizBI/FCJsXUvoY50wIcb0YrLs6m9xUmhQ7gBjDtKBXrSZzruCLz1UUor7Kyv/VZrgpEscNKR7lOIfKATI/JmwIDAQABAoIBAQDC9ssL/XJQ4kgQFejdCzAP2zrtabf3Ng2mv1tkZaxTdlEDQnA7cZW491Tj7XOE6foa/ZUXV1HvfG8cpQyW7u2PelFgljFiwIQPAlIIBnYGnoyGIOck2yJUpL8vaaCnxzQrVBHt5CVo4XxmI8G4dn4JThvn2k/9tHAIH8CpnwrYtvjXlx9Zz2XK9rGMUzvDphDU4B2u4cJ42fSOD/yJFQKA2z7eWKC8/2vPHYjgGuEli7GHX0UWBBjA3BQMOJbF/XhrJVkttAbLnkVgpd3KFZiUDOAWb9r6vyJAyW0yzCCSfCHTIN/v+pAwN3uEnZSVAnySWvE9LK0tck/mNQY9epSJAoGBAPy2rUFPdsLQsbWsWl9N10Qtxc5ufTfwingFh8t3DaFmalwxRummApM1zB13bx2nIIrPzoB6iLnBUqgL7NYnntZcWO6YdrWi1Vc8vroWDK0/8K5QFhywdg3iCZNw9Szk4xeVUBp+J3CiBfWFqdOFNc6IwyWpCI30plaOXQ6PF7SFAoGBAMgLjwfMBhH9Cr9V47q3mZi+DG2DSplq+6RsPlHvsevdtpEq2puBLPnZkoEwHouHcKOW/3zELq/Th3HQ6pSZRb9YE5e5BoyQdCTmiyHflLTHIvxnZE/JtIRWjMp5pqzQTUJnPmIx14XtVw8QeJeO8nM1jI1cm8EpsdUqF0GwnW+fAoGAPFXlEaXGRgcLlsN6pCxyi92dz4aDsPpmJPe1DzfycimAlVhShPBUxw7eF97vEZZYZ6vrsrMOt8LXWDXYqty0yJstxHF0TjN9WiGvq8Ad8LNGZGfMj7b1yKTCAojCkKBpM7U7dcfO5M5aO99Yx56TVSdD6FbuKu5RVHMS9qnlT/0CgYBiFfjNs+YOD+qjInQE610unZDuaX+8dc5pMoHkHwk4Q3/u8fo6YT4yS9If8f4oIALE05b2ECNsJuW0kuFSd0zRo1gH/rsNVQ79wOTbeQsrELFA3Vk8HgSbFuz1omxtM72OH51g/FtW2abkRgk7FuYeQ1VlQtFEBzOg94BrvzOStQKBgGABHysR201jKXqUQTnrylS7huzRrQQAaZMIMlbatGTtzzHjPwPrSbjDl1xIOSxVb0wHpyAecs5OVy0lQTGpLOat33DqHUA0c36emmmpf9cF4hkYl1YoghjqE+pVPtg+vx3DfxukY0FLz9qCpXtW7i4dJhJ7tt9Y7wcRMfShGcIb" + ], + "certificate": [ + "MIICrzCCAZcCBgF1UFlhhjANBgkqhkiG9w0BAQsFADAbMRkwFwYDVQQDDBBTZWVkU2FtcGxlc1JlYWxtMB4XDTIwMTAyMjEyNDUzNloXDTMwMTAyMjEyNDcxNlowGzEZMBcGA1UEAwwQU2VlZFNhbXBsZXNSZWFsbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMV6IGY1EPqT9GCMIOoNo3wBzKOEuDxqm8GeBVMUZhlbJhBu7G0xHf0huJTewEH0Gk6RVwEGwD5ZPv8QHKk+N/Jzy6SJ9uVSFuwcqlCIilc6aDG3MmCu4DdbM5cHPqQ8RAvzH1r9JQJz4FSTW1hzBMJIUoqLBjc+LxO/CWb3wKGRSlJmWOnaEcuO2yOVXxQHKHKEeHe3EjgwkH1kdj7odfctSEmXscP55EOpHQpxMMtC47je0nnV9sMyvTCXkf8qdXoswSPxQibF1L6GOdMCHG9GKy7OpvcVJoUO4AYw7SgV60mc67gi89VFKK+ysr/1Wa4KRLHDSke5TiHygEyPyZsCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAEcWx1OJfmgIgQCGQFXdaDoAUOfLfohDZ++RKenz55guqZLXFS7EfzaRv2TlauvkbKzLHzVMyn3cc39LWxAicgU+5rRvmXzL/4+mR2BxPVfcJgOwSX1GPuP624kGsV33d5ef7n6buxJC0ZcoKsPzpmXeQ0ak/aPKsmPQSPmEX/2dkKSHwx26+ioPppp9Iq/7WTC0QOsRnEk+UP2XPBJjg7Q1XKXeZ2ECNuO6NQAKgsLEuFfi1WvUA7L3itF9Tz5pJyO8y/EJqe2LrXcfv/dvWzQ0I+CpMkb48hjTRdLjSyh+7Bssk9qItb16AyEL9yWV76sFOAaUWg5k00sTxaObUnw==" + ], + "priority": [ + "100" + ] + } + }, + { + "id": "4faabd17-a032-4419-8043-67e2d238eef6", + "name": "aes-generated", + "providerId": "aes-generated", + "subComponents": {}, + "config": { + "kid": [ + "2d9654c0-6f8a-4ca2-8635-f38667e0b0c0" + ], + "secret": [ + "-0g9CF6e99aA4kFUzWwvVw" + ], + "priority": [ + "100" + ] + } + } + ] + }, + "internationalizationEnabled": false, + "supportedLocales": [], + "authenticationFlows": [ + { + "id": "94de2b11-4c77-4f4e-b63c-1b36f6455ea4", + "alias": "Account verification options", + "description": "Method with which to verity the existing account", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "idp-email-verification", + "requirement": "ALTERNATIVE", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "requirement": "ALTERNATIVE", + "priority": 20, + "flowAlias": "Verify Existing Account by Re-authentication", + "userSetupAllowed": false, + "autheticatorFlow": true + } + ] + }, + { + "id": "77582840-f466-4977-9de8-b99fe5fd8061", + "alias": "Authentication Options", + "description": "Authentication options.", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "basic-auth", + "requirement": "REQUIRED", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "basic-auth-otp", + "requirement": "DISABLED", + "priority": 20, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "auth-spnego", + "requirement": "DISABLED", + "priority": 30, + "userSetupAllowed": false, + "autheticatorFlow": false + } + ] + }, + { + "id": "31759064-4b75-4d08-bb45-5bad882e4d3b", + "alias": "Browser - Conditional OTP", + "description": "Flow to determine if the OTP is required for the authentication", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "conditional-user-configured", + "requirement": "REQUIRED", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "auth-otp-form", + "requirement": "REQUIRED", + "priority": 20, + "userSetupAllowed": false, + "autheticatorFlow": false + } + ] + }, + { + "id": "92f74c03-b7d3-41cd-877b-123917fe1ac1", + "alias": "Direct Grant - Conditional OTP", + "description": "Flow to determine if the OTP is required for the authentication", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "conditional-user-configured", + "requirement": "REQUIRED", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "direct-grant-validate-otp", + "requirement": "REQUIRED", + "priority": 20, + "userSetupAllowed": false, + "autheticatorFlow": false + } + ] + }, + { + "id": "e733bca7-9e3d-44fd-8cd2-f5b4d5728dd9", + "alias": "First broker login - Conditional OTP", + "description": "Flow to determine if the OTP is required for the authentication", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "conditional-user-configured", + "requirement": "REQUIRED", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "auth-otp-form", + "requirement": "REQUIRED", + "priority": 20, + "userSetupAllowed": false, + "autheticatorFlow": false + } + ] + }, + { + "id": "f2ae4741-e6ef-432e-8a87-59cb97c76b6c", + "alias": "Handle Existing Account", + "description": "Handle what to do if there is existing account with same email/username like authenticated identity provider", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "idp-confirm-link", + "requirement": "REQUIRED", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "requirement": "REQUIRED", + "priority": 20, + "flowAlias": "Account verification options", + "userSetupAllowed": false, + "autheticatorFlow": true + } + ] + }, + { + "id": "de9e9b6a-f12e-453f-93e6-939ed5e952c5", + "alias": "Reset - Conditional OTP", + "description": "Flow to determine if the OTP should be reset or not. Set to REQUIRED to force.", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "conditional-user-configured", + "requirement": "REQUIRED", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "reset-otp", + "requirement": "REQUIRED", + "priority": 20, + "userSetupAllowed": false, + "autheticatorFlow": false + } + ] + }, + { + "id": "128f7598-c857-42bf-b8a2-f30fe4c0a5a1", + "alias": "User creation or linking", + "description": "Flow for the existing/non-existing user alternatives", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticatorConfig": "create unique user config", + "authenticator": "idp-create-user-if-unique", + "requirement": "ALTERNATIVE", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "requirement": "ALTERNATIVE", + "priority": 20, + "flowAlias": "Handle Existing Account", + "userSetupAllowed": false, + "autheticatorFlow": true + } + ] + }, + { + "id": "503aa81c-1189-445e-9dd9-5a3c03620018", + "alias": "Verify Existing Account by Re-authentication", + "description": "Reauthentication of existing account", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "idp-username-password-form", + "requirement": "REQUIRED", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "requirement": "CONDITIONAL", + "priority": 20, + "flowAlias": "First broker login - Conditional OTP", + "userSetupAllowed": false, + "autheticatorFlow": true + } + ] + }, + { + "id": "3cbab7e0-6e8f-4109-8388-a12c886c3e3c", + "alias": "browser", + "description": "browser based authentication", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "auth-cookie", + "requirement": "ALTERNATIVE", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "auth-spnego", + "requirement": "DISABLED", + "priority": 20, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "identity-provider-redirector", + "requirement": "ALTERNATIVE", + "priority": 25, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "requirement": "ALTERNATIVE", + "priority": 30, + "flowAlias": "forms", + "userSetupAllowed": false, + "autheticatorFlow": true + } + ] + }, + { + "id": "9e8e638d-9728-4684-ad5d-0bc8b0996faf", + "alias": "clients", + "description": "Base authentication for clients", + "providerId": "client-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "client-secret", + "requirement": "ALTERNATIVE", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "client-jwt", + "requirement": "ALTERNATIVE", + "priority": 20, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "client-secret-jwt", + "requirement": "ALTERNATIVE", + "priority": 30, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "client-x509", + "requirement": "ALTERNATIVE", + "priority": 40, + "userSetupAllowed": false, + "autheticatorFlow": false + } + ] + }, + { + "id": "f7d1a6be-7774-4b45-a843-98d56ce6ce04", + "alias": "direct grant", + "description": "OpenID Connect Resource Owner Grant", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "direct-grant-validate-username", + "requirement": "REQUIRED", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "direct-grant-validate-password", + "requirement": "REQUIRED", + "priority": 20, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "requirement": "CONDITIONAL", + "priority": 30, + "flowAlias": "Direct Grant - Conditional OTP", + "userSetupAllowed": false, + "autheticatorFlow": true + } + ] + }, + { + "id": "f18f2609-e4d2-488a-9e02-7c5ed3a9e127", + "alias": "docker auth", + "description": "Used by Docker clients to authenticate against the IDP", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "docker-http-basic-authenticator", + "requirement": "REQUIRED", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + } + ] + }, + { + "id": "591f2a82-2cf5-4bb0-8e2a-4711b690c418", + "alias": "first broker login", + "description": "Actions taken after first broker login with identity provider account, which is not yet linked to any Keycloak account", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticatorConfig": "review profile config", + "authenticator": "idp-review-profile", + "requirement": "REQUIRED", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "requirement": "REQUIRED", + "priority": 20, + "flowAlias": "User creation or linking", + "userSetupAllowed": false, + "autheticatorFlow": true + } + ] + }, + { + "id": "093ea886-40fb-4c67-806d-a301061bf7fb", + "alias": "forms", + "description": "Username, password, otp and other auth forms.", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "auth-username-password-form", + "requirement": "REQUIRED", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "requirement": "CONDITIONAL", + "priority": 20, + "flowAlias": "Browser - Conditional OTP", + "userSetupAllowed": false, + "autheticatorFlow": true + } + ] + }, + { + "id": "d23c1ff6-57cd-45ff-a3ee-3a373f332a70", + "alias": "http challenge", + "description": "An authentication flow based on challenge-response HTTP Authentication Schemes", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "no-cookie-redirect", + "requirement": "REQUIRED", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "requirement": "REQUIRED", + "priority": 20, + "flowAlias": "Authentication Options", + "userSetupAllowed": false, + "autheticatorFlow": true + } + ] + }, + { + "id": "01d17ba4-9fca-40a7-9b83-9bfe349718db", + "alias": "registration", + "description": "registration flow", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "registration-page-form", + "requirement": "REQUIRED", + "priority": 10, + "flowAlias": "registration form", + "userSetupAllowed": false, + "autheticatorFlow": true + } + ] + }, + { + "id": "c9ebf671-8120-4b71-a04b-94e0d828747c", + "alias": "registration form", + "description": "registration form", + "providerId": "form-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "registration-user-creation", + "requirement": "REQUIRED", + "priority": 20, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "registration-profile-action", + "requirement": "REQUIRED", + "priority": 40, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "registration-password-action", + "requirement": "REQUIRED", + "priority": 50, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "registration-recaptcha-action", + "requirement": "DISABLED", + "priority": 60, + "userSetupAllowed": false, + "autheticatorFlow": false + } + ] + }, + { + "id": "c9301068-d62b-4f35-b7dd-35caef817f96", + "alias": "reset credentials", + "description": "Reset credentials for a user if they forgot their password or something", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "reset-credentials-choose-user", + "requirement": "REQUIRED", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "reset-credential-email", + "requirement": "REQUIRED", + "priority": 20, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "reset-password", + "requirement": "REQUIRED", + "priority": 30, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "requirement": "CONDITIONAL", + "priority": 40, + "flowAlias": "Reset - Conditional OTP", + "userSetupAllowed": false, + "autheticatorFlow": true + } + ] + }, + { + "id": "59c64549-3d8b-42b8-a8d4-8b06e008b8eb", + "alias": "saml ecp", + "description": "SAML ECP Profile Authentication Flow", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "http-basic-authenticator", + "requirement": "REQUIRED", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + } + ] + } + ], + "authenticatorConfig": [ + { + "id": "a3333280-e46c-485d-8051-6dec9da86246", + "alias": "create unique user config", + "config": { + "require.password.update.after.registration": "false" + } + }, + { + "id": "42284fc3-29b4-46ac-9c15-203686f02b3e", + "alias": "review profile config", + "config": { + "update.profile.on.first.login": "missing" + } + } + ], + "requiredActions": [ + { + "alias": "CONFIGURE_TOTP", + "name": "Configure OTP", + "providerId": "CONFIGURE_TOTP", + "enabled": true, + "defaultAction": false, + "priority": 10, + "config": {} + }, + { + "alias": "terms_and_conditions", + "name": "Terms and Conditions", + "providerId": "terms_and_conditions", + "enabled": false, + "defaultAction": false, + "priority": 20, + "config": {} + }, + { + "alias": "UPDATE_PASSWORD", + "name": "Update Password", + "providerId": "UPDATE_PASSWORD", + "enabled": true, + "defaultAction": false, + "priority": 30, + "config": {} + }, + { + "alias": "UPDATE_PROFILE", + "name": "Update Profile", + "providerId": "UPDATE_PROFILE", + "enabled": true, + "defaultAction": false, + "priority": 40, + "config": {} + }, + { + "alias": "VERIFY_EMAIL", + "name": "Verify Email", + "providerId": "VERIFY_EMAIL", + "enabled": true, + "defaultAction": false, + "priority": 50, + "config": {} + }, + { + "alias": "update_user_locale", + "name": "Update User Locale", + "providerId": "update_user_locale", + "enabled": true, + "defaultAction": false, + "priority": 1000, + "config": {} + } + ], + "browserFlow": "browser", + "registrationFlow": "registration", + "directGrantFlow": "direct grant", + "resetCredentialsFlow": "reset credentials", + "clientAuthenticationFlow": "clients", + "dockerAuthenticationFlow": "docker auth", + "attributes": { + "clientOfflineSessionMaxLifespan": "0", + "clientSessionIdleTimeout": "0", + "clientSessionMaxLifespan": "0", + "clientOfflineSessionIdleTimeout": "0" + }, + "keycloakVersion": "11.0.2", + "userManagedAccessAllowed": false + } diff --git a/addons/oauth/auth-code-flow/README.md b/addons/oauth/auth-code-flow/README.md new file mode 100644 index 0000000..7a0d68b --- /dev/null +++ b/addons/oauth/auth-code-flow/README.md @@ -0,0 +1,47 @@ +# OAuth addon - Authorization code flow sample + +## Sample use case + +This samples shows how to configure Oauth addon in your seedstack application for an Authorization code flow usage. + +This sample runs a seedstack application exposing a protected resource. + +The OAuth configuration is backend side. When the request is made to a protected resource, the backend sends an authenticaton request to the identity provider and gets the response sending a http error code if user is not authenticated or has a denied access. + +The application.yaml configuration file show how the addon is configured for this use case. + +* url patterns are secured with oauth and oauthCallback filters +* the addon configuration shows the required items : + * discoveryDocument : auth server OpenID end-points + * redirect : callback redirect + * scopes : Required scopes, for this sample CodeFlowScope is required + * clientId : The auth-server registered client identifier related to this this sample + * clientSecret : The auth-server registered client secret + * allowedAudiences : Authorizes the auth-server "account" audience + * autoFetchUserInfo : Set to true, the application retrieves user information automatically. + +The org.seedstack.samples.oauth.ProfileResource class is the secured end-point of this sample. + + +## Running the sample + +### Prerequisite + +In order to run this sample, the provided authentication and access management server must be running. (Please see [this page](https://github.com/seedstack/samples/tree/master/addons/oauth) for more details) + +This sample starts a server listening on the 8090 port. Make sure no other application is using this port. + +### Run the sample + +Run this maven project main class : org.seedstack.samples.oauth.Demo + +The seedstack application starts listening ton the 8090 port. + +From your browser, make a browse to http://localhost:8090 and click on *Click here to log in.* + +At this point, the auth server should request you for authentication. + +Enter the following credentials : + +* **User "John Doe" password : "P4$$word"** : Pre-configured user with agreement on the required "CodeFlowScope" scope. The displayed page should provide information on the user. +* **User "Jane Smith" password : "P4$$word"** : pre-configured user with no agreement on the required "CodeFlowScope" scope, **refuse the scope**, you should see an error page. \ No newline at end of file diff --git a/addons/oauth/auth-code-flow/pom.xml b/addons/oauth/auth-code-flow/pom.xml new file mode 100644 index 0000000..169895c --- /dev/null +++ b/addons/oauth/auth-code-flow/pom.xml @@ -0,0 +1,65 @@ + + + + 4.0.0 + + + org.seedstack.samples + samples + 20.11-SNAPSHOT + ../../../pom.xml + + + oauth-code-flow-sample + + + + org.seedstack.seed + seed-rest-jersey2 + + + org.seedstack.seed + seed-web-undertow + + + org.seedstack.seed + seed-web-security + + + org.seedstack.addons.oauth + oauth + + + ch.qos.logback + logback-classic + ${logback.version} + + + + org.seedstack.seed + seed-testing-junit4 + test + + + org.assertj + assertj-core + ${assertj.version} + test + + + io.rest-assured + rest-assured + ${rest-assured.version} + test + + + diff --git a/addons/oauth/auth-code-flow/src/main/java/org/seedstack/samples/oauth/Demo.java b/addons/oauth/auth-code-flow/src/main/java/org/seedstack/samples/oauth/Demo.java new file mode 100644 index 0000000..94a6666 --- /dev/null +++ b/addons/oauth/auth-code-flow/src/main/java/org/seedstack/samples/oauth/Demo.java @@ -0,0 +1,21 @@ +/* + * Copyright © 2013-2020, The SeedStack authors + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.seedstack.samples.oauth; + +import org.seedstack.seed.core.Seed; + +public class Demo { + /** + * Launches the SeedStack demo server exposing the protected request + * @param args Main arguments + * @throws Exception In case of problems + */ + public static void main(String[] args) throws Exception { + Seed.getLauncher().launch(args); + } +} diff --git a/addons/oauth/auth-code-flow/src/main/java/org/seedstack/samples/oauth/ProfileRepresentation.java b/addons/oauth/auth-code-flow/src/main/java/org/seedstack/samples/oauth/ProfileRepresentation.java new file mode 100644 index 0000000..4e53d75 --- /dev/null +++ b/addons/oauth/auth-code-flow/src/main/java/org/seedstack/samples/oauth/ProfileRepresentation.java @@ -0,0 +1,70 @@ +/* + * Copyright © 2013-2020, The SeedStack authors + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.seedstack.samples.oauth; + +import java.util.Map; + +/** + * A user's profile representation to be Mapped to JSON + */ +public class ProfileRepresentation { + private String userId; + private String firstName; + private String lastName; + private String fullName; + private String pictureUrl; + private Map principals; + + public String getUserId() { + return userId; + } + + public void setUserId(String userId) { + this.userId = userId; + } + + public String getFirstName() { + return firstName; + } + + public void setFirstName(String firstName) { + this.firstName = firstName; + } + + public String getLastName() { + return lastName; + } + + public void setLastName(String lastName) { + this.lastName = lastName; + } + + public String getFullName() { + return fullName; + } + + public void setFullName(String fullName) { + this.fullName = fullName; + } + + public String getPictureUrl() { + return pictureUrl; + } + + public void setPictureUrl(String pictureUrl) { + this.pictureUrl = pictureUrl; + } + + public Map getPrincipals() { + return principals; + } + + public void setPrincipals(Map principals) { + this.principals = principals; + } +} diff --git a/addons/oauth/auth-code-flow/src/main/java/org/seedstack/samples/oauth/ProfileResource.java b/addons/oauth/auth-code-flow/src/main/java/org/seedstack/samples/oauth/ProfileResource.java new file mode 100644 index 0000000..b2d2b65 --- /dev/null +++ b/addons/oauth/auth-code-flow/src/main/java/org/seedstack/samples/oauth/ProfileResource.java @@ -0,0 +1,73 @@ +/* + * Copyright © 2013-2020, The SeedStack authors + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.seedstack.samples.oauth; + +import org.apache.shiro.authz.UnauthenticatedException; +import org.seedstack.seed.Logging; +import org.seedstack.seed.security.RequiresPermissions; +import org.seedstack.seed.security.SecuritySupport; +import org.seedstack.seed.security.principals.PrincipalProvider; +import org.seedstack.seed.security.principals.Principals; +import org.seedstack.seed.security.principals.SimplePrincipalProvider; +import org.slf4j.Logger; + +import javax.inject.Inject; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; + +@Path("/profile") +public class ProfileResource { + /**Inject Security support to access user details*/ + @Inject + private SecuritySupport securitySupport; + + @Logging + private Logger logger; + + /** + * This is the protected resource.
+ * For this sample it only returns a profile representation of the connected user + * @return ProfileRepresentation + */ + @GET + @Produces("application/json") + @RequiresPermissions("CodeFlowScope") + public ProfileRepresentation sayHello() { + logger.info("Request received and access granted to protected resource"); + ProfileRepresentation profileRepresentation = new ProfileRepresentation(); + profileRepresentation.setUserId(Optional.ofNullable(securitySupport.getIdentityPrincipal()) + .map(PrincipalProvider::get) + .map(Object::toString) + .orElseThrow(() -> new UnauthenticatedException("No user identity available"))); + Optional.ofNullable(securitySupport.getSimplePrincipalByName(Principals.FIRST_NAME)) + .map(SimplePrincipalProvider::get) + .ifPresent(profileRepresentation::setFirstName); + Optional.ofNullable(securitySupport.getSimplePrincipalByName(Principals.LAST_NAME)) + .map(SimplePrincipalProvider::get) + .ifPresent(profileRepresentation::setLastName); + Optional.ofNullable(securitySupport.getSimplePrincipalByName(Principals.FULL_NAME)) + .map(SimplePrincipalProvider::get) + .ifPresent(profileRepresentation::setFullName); + Optional.ofNullable(securitySupport.getSimplePrincipalByName("picture")) + .map(SimplePrincipalProvider::get) + .ifPresent(profileRepresentation::setPictureUrl); + + Map principals = new HashMap<>(); + for (SimplePrincipalProvider simplePrincipalProvider : securitySupport.getSimplePrincipals()) { + principals.put(simplePrincipalProvider.getName(), simplePrincipalProvider.getValue()); + } + profileRepresentation.setPrincipals(principals); + + logger.info("Ending request for user : {}", profileRepresentation.getFullName()); + return profileRepresentation; + } +} diff --git a/addons/oauth/auth-code-flow/src/main/resources/META-INF/resources/index.html b/addons/oauth/auth-code-flow/src/main/resources/META-INF/resources/index.html new file mode 100644 index 0000000..a27a583 --- /dev/null +++ b/addons/oauth/auth-code-flow/src/main/resources/META-INF/resources/index.html @@ -0,0 +1,95 @@ + + + + + + + + + +
+
+
+
+

Seedstack Oauth addon - Authorization code flow sample

+
+

Welcome to the seedstack OAuth addon - Authorization code flow sample !

+

Please see The README file for more details about this sample.

+
+

Please, ensure the auth server is running on port 8080.

+
+ +
+
+

For reminder :

+

User "John Doe" Password "P4$$word" : This user as granted access to the required scope.

+

User "Jane Smith" Password "P4$$word" : Do not grant access to the required scope to view access denied page.

+
+ + +
+
+
+ + + \ No newline at end of file diff --git a/addons/oauth/auth-code-flow/src/main/resources/META-INF/resources/mascot-happy.png b/addons/oauth/auth-code-flow/src/main/resources/META-INF/resources/mascot-happy.png new file mode 100644 index 0000000..21248e0 Binary files /dev/null and b/addons/oauth/auth-code-flow/src/main/resources/META-INF/resources/mascot-happy.png differ diff --git a/addons/oauth/auth-code-flow/src/main/resources/application.yaml b/addons/oauth/auth-code-flow/src/main/resources/application.yaml new file mode 100644 index 0000000..4d56a40 --- /dev/null +++ b/addons/oauth/auth-code-flow/src/main/resources/application.yaml @@ -0,0 +1,43 @@ +# +# Copyright © 2013-2020, The SeedStack authors +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# + +logging: + level: INFO + +application: + id: oauth-sample + +web: + server: + port: 8090 + +rest: + path: /api + + +#Security configuration set to use the OAuth addon +security: + realms: OAuthRealm + web: + urls: + - pattern: /logout + filters: logout + - pattern: /callback + filters: oauthCallback + - pattern: /api/** + filters: oauth + + #OAuth addon configuration for an Authorization code flow usage + oauth: + discoveryDocument: http://localhost:8080/auth/realms/SeedSamplesRealm/.well-known/openid-configuration + redirect: http://localhost:8090/callback + scopes: [email openid profile CodeFlowScope] + clientId: SeedStackClientCodeFlow + clientSecret: 3545c109-9cf0-4ccf-82aa-89464f798844 + allowedAudiences: account + autoFetchUserInfo: true diff --git a/addons/oauth/auth-code-pkce-flow/README.md b/addons/oauth/auth-code-pkce-flow/README.md new file mode 100644 index 0000000..8f60036 --- /dev/null +++ b/addons/oauth/auth-code-pkce-flow/README.md @@ -0,0 +1,60 @@ +# OAuth addon - Authorization Code Flow with Proof Key for Code Exchange (PKCE) sample + +## Sample use case + +This samples shows how to configure Oauth addon in your seedstack application for an Authorization Code Flow with Proof Key for Code Exchange usage. + +This sample runs a seedstack application exposing a protected resource. + +The Oauth configuration is both on frontend and backend sides. + +The Frontend client proceeds to authentication with the auth server and then requests the backend with an auth token. The Backend validates the token an gives access (or not ) to the protected resource. + +### Frontend configuration + +Not part of seedstack, the frontend oauth configuration depends on the used technology. + +In this sample, the frontend configuration set is : + +*client_id : client identifier as registered in the auth server +*redirect_uri : the redirection uri +*authorization_endpoint : Auth server endpoint for authentication +*token_endpoint : Auth server end point for tokens +*requested_scopes : The user's requested scopes +*audience : the request audience +*code_challenge_method: The crypting method for the code challenge verification + +### Backend configuration + +The application.yaml configuration file show how the addon is configured for this use case. + +The resource if protected using the oauth security filter. As the authentication is made frontend side, there is no need for a oauth callback filter on backend side. + +The OAuth addon parameters are set : +*discoveryDocument : the auth server openId discovery document +*allowedAudiences : the concerned audiences for this resource +*scopes : the user's requested scopes for this sample pkceScope is required + +## Running the sample + +### Prerequisite + +In order to run this sample, the provided authentication and access management server must be running. (Please see [this page](https://github.com/seedstack/samples/tree/master/addons/oauth) for more details) + +This sample starts a server listening on the 8090 port. Make sure no other application is using this port. + + +### Run the sample + +Run this maven project main class : org.seedstack.samples.oauth.Demo + +The seedStack application starts listening ton the 8090 port. + +From your browser, make a request to http://localhost:8090 and click on "Click to Sign In" + +At this point, the auth server should request you for authentication. + +Enter the following credentials : + +* **User "John Doe" password : "P4$$word"** : Pre-configured user with accurate scope. The displayed page should provide information on the user. +* **User "Jane Smith" password : "P4$$word"** : Pre-configured user not associated to the required scope. The displayed page should be an access denied page. \ No newline at end of file diff --git a/addons/oauth/auth-code-pkce-flow/pom.xml b/addons/oauth/auth-code-pkce-flow/pom.xml new file mode 100644 index 0000000..52148ee --- /dev/null +++ b/addons/oauth/auth-code-pkce-flow/pom.xml @@ -0,0 +1,65 @@ + + + + 4.0.0 + + + org.seedstack.samples + samples + 20.11-SNAPSHOT + ../../../pom.xml + + + oauth-auth-code-pkce-sample + + + + org.seedstack.seed + seed-rest-jersey2 + + + org.seedstack.seed + seed-web-undertow + + + org.seedstack.seed + seed-web-security + + + org.seedstack.addons.oauth + oauth + + + ch.qos.logback + logback-classic + ${logback.version} + + + + org.seedstack.seed + seed-testing-junit4 + test + + + org.assertj + assertj-core + ${assertj.version} + test + + + io.rest-assured + rest-assured + ${rest-assured.version} + test + + + diff --git a/addons/oauth/auth-code-pkce-flow/src/main/java/org/seedstack/samples/oauth/Demo.java b/addons/oauth/auth-code-pkce-flow/src/main/java/org/seedstack/samples/oauth/Demo.java new file mode 100644 index 0000000..e743b7d --- /dev/null +++ b/addons/oauth/auth-code-pkce-flow/src/main/java/org/seedstack/samples/oauth/Demo.java @@ -0,0 +1,21 @@ +/* + * Copyright © 2013-2020, The SeedStack authors + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.seedstack.samples.oauth; + +import org.seedstack.seed.core.Seed; + +public class Demo { + /** + * Launches the Seedstack sample server + * @param args main methid arguments + * @throws Exception In case of problem + */ + public static void main(String[] args) throws Exception { + Seed.getLauncher().launch(args); + } +} diff --git a/addons/oauth/auth-code-pkce-flow/src/main/java/org/seedstack/samples/oauth/ProfileRepresentation.java b/addons/oauth/auth-code-pkce-flow/src/main/java/org/seedstack/samples/oauth/ProfileRepresentation.java new file mode 100644 index 0000000..9658e83 --- /dev/null +++ b/addons/oauth/auth-code-pkce-flow/src/main/java/org/seedstack/samples/oauth/ProfileRepresentation.java @@ -0,0 +1,69 @@ +/* + * Copyright © 2013-2020, The SeedStack authors + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.seedstack.samples.oauth; + +import java.util.Map; +/** + * A user's profile representation to be Mapped to JSON + */ +public class ProfileRepresentation { + private String userId; + private String firstName; + private String lastName; + private String fullName; + private String pictureUrl; + private Map principals; + + public String getUserId() { + return userId; + } + + public void setUserId(String userId) { + this.userId = userId; + } + + public String getFirstName() { + return firstName; + } + + public void setFirstName(String firstName) { + this.firstName = firstName; + } + + public String getLastName() { + return lastName; + } + + public void setLastName(String lastName) { + this.lastName = lastName; + } + + public String getFullName() { + return fullName; + } + + public void setFullName(String fullName) { + this.fullName = fullName; + } + + public String getPictureUrl() { + return pictureUrl; + } + + public void setPictureUrl(String pictureUrl) { + this.pictureUrl = pictureUrl; + } + + public Map getPrincipals() { + return principals; + } + + public void setPrincipals(Map principals) { + this.principals = principals; + } +} diff --git a/addons/oauth/auth-code-pkce-flow/src/main/java/org/seedstack/samples/oauth/ProfileResource.java b/addons/oauth/auth-code-pkce-flow/src/main/java/org/seedstack/samples/oauth/ProfileResource.java new file mode 100644 index 0000000..4da7b79 --- /dev/null +++ b/addons/oauth/auth-code-pkce-flow/src/main/java/org/seedstack/samples/oauth/ProfileResource.java @@ -0,0 +1,67 @@ +/* + * Copyright © 2013-2020, The SeedStack authors + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.seedstack.samples.oauth; + +import org.apache.shiro.authz.UnauthenticatedException; +import org.seedstack.seed.security.RequiresPermissions; +import org.seedstack.seed.security.SecuritySupport; +import org.seedstack.seed.security.principals.PrincipalProvider; +import org.seedstack.seed.security.principals.Principals; +import org.seedstack.seed.security.principals.SimplePrincipalProvider; + +import javax.inject.Inject; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; + +@Path("/profile") +public class ProfileResource { + /**Inject security support to access user details*/ + @Inject + private SecuritySupport securitySupport; + + + /** + * This is the protected resource.
+ * For this sample it only returns a profile representation of the connected user + * @return ProfileRepresentation + */ + @GET + @Produces("application/json") + @RequiresPermissions("pkceScope") + public ProfileRepresentation profile() { + ProfileRepresentation profileRepresentation = new ProfileRepresentation(); + profileRepresentation.setUserId(Optional.ofNullable(securitySupport.getIdentityPrincipal()) + .map(PrincipalProvider::get) + .map(Object::toString) + .orElseThrow(() -> new UnauthenticatedException("No user identity available"))); + Optional.ofNullable(securitySupport.getSimplePrincipalByName(Principals.FIRST_NAME)) + .map(SimplePrincipalProvider::get) + .ifPresent(profileRepresentation::setFirstName); + Optional.ofNullable(securitySupport.getSimplePrincipalByName(Principals.LAST_NAME)) + .map(SimplePrincipalProvider::get) + .ifPresent(profileRepresentation::setLastName); + Optional.ofNullable(securitySupport.getSimplePrincipalByName(Principals.FULL_NAME)) + .map(SimplePrincipalProvider::get) + .ifPresent(profileRepresentation::setFullName); + Optional.ofNullable(securitySupport.getSimplePrincipalByName("picture")) + .map(SimplePrincipalProvider::get) + .ifPresent(profileRepresentation::setPictureUrl); + + Map principals = new HashMap<>(); + for (SimplePrincipalProvider simplePrincipalProvider : securitySupport.getSimplePrincipals()) { + principals.put(simplePrincipalProvider.getName(), simplePrincipalProvider.getValue()); + } + profileRepresentation.setPrincipals(principals); + + return profileRepresentation; + } +} diff --git a/addons/oauth/auth-code-pkce-flow/src/main/resources/META-INF/resources/index.html b/addons/oauth/auth-code-pkce-flow/src/main/resources/META-INF/resources/index.html new file mode 100644 index 0000000..41eb6d0 --- /dev/null +++ b/addons/oauth/auth-code-pkce-flow/src/main/resources/META-INF/resources/index.html @@ -0,0 +1,270 @@ + +OAuth addom sample - Authorization Code flow PKCE + + + + + + + + + +
+
+
+
+

Seedstack Oauth addon - Authorization code flow PKCE sample

+
+

Welcome to the seedstack OAuth addon - Authorization Code Flow with Proof Key for Code Exchange (PKCE) sample.

+

Please see The README file for more details about this sample.

+
+

Please, ensure the auth server is running on port 8080.

+
+ +
+
+

For reminder :

+

User "John Doe" Password "P4$$word" : This user as granted access to the required scope.

+

User "Jane Smith" Password "P4$$word" : This user does not have the required scope. use it to view access denied page.

+
+ + +
+
+
+ + + + diff --git a/addons/oauth/auth-code-pkce-flow/src/main/resources/META-INF/resources/mascot-happy.png b/addons/oauth/auth-code-pkce-flow/src/main/resources/META-INF/resources/mascot-happy.png new file mode 100644 index 0000000..21248e0 Binary files /dev/null and b/addons/oauth/auth-code-pkce-flow/src/main/resources/META-INF/resources/mascot-happy.png differ diff --git a/addons/oauth/auth-code-pkce-flow/src/main/resources/application.yaml b/addons/oauth/auth-code-pkce-flow/src/main/resources/application.yaml new file mode 100644 index 0000000..365ac5f --- /dev/null +++ b/addons/oauth/auth-code-pkce-flow/src/main/resources/application.yaml @@ -0,0 +1,40 @@ +# +# Copyright © 2013-2020, The SeedStack authors +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# + +logging: + level: INFO + +#The Sample's Authentication server listens on the 8080 port, we are using here port 8090 +web: + server: + port: 8090 + +application: + id: oauth-sample + +rest: + path: /api + +#The api is secured with OAuth configuration +security: + realms: OAuthRealm + web: + urls: + - pattern: /logout + filters: logout + - pattern: /api/** + filters: oauth + +#The OAuth configuration required for a PKCE exchange +#The client secret and ID are managed frontend side. +#The PKCE sample configuration validates the bearer token coming with the request Authorization header + oauth: + discoveryDocument: http://localhost:8080/auth/realms/SeedSamplesRealm/.well-known/openid-configuration + allowedAudiences: ["http://localhost:8090/api/profile","account"] + scopes: [openid, email, profile, pkceScope] + autoFetchUserInfo: true diff --git a/addons/oauth/client-credentials-flow-cli/README.md b/addons/oauth/client-credentials-flow-cli/README.md new file mode 100644 index 0000000..0931d1d --- /dev/null +++ b/addons/oauth/client-credentials-flow-cli/README.md @@ -0,0 +1,47 @@ +# OAuth addon - Authorization with Client Credentials Flow + +## Sample use case + +The client credential flow is used in a machine to machine exchange. In this sample, the client is a seedstack CLI +application requesting an authentication token to the auth server. + +Once the token recieved, the client makes a request to a distant protected resource authenticated with a +"Authorization : Bearer [TOKEN]" header. + +On the server side, there is a seedstack web application exposing a protected resource, the application recieves the +request and validates the token to grant access to the resource. + +### Client configuration + +On the client side, in the application.yaml file, oauth addon if configured with : + +* discoveryDocument : The auth server openID discovery document +* clientId : Client identifier as registered in the auth server as allowing service account (client credential flow) usage +* clientSecret : The clientSecret registered in the auth server + +### Server configuration + +On server side, the oauth addon is configured in the application.yaml file with : + +* discoveryDocument : The auth server openID discovery document +* allowedAudiences : The audiences concerned + +## Running the sample + +### Prerequisite + +In order to run this sample, the provided authentication and access management server must be running. (Please +see [this page](https://github.com/seedstack/samples/tree/master/addons/oauth) for more details) + +This sample starts a server listening on the 8090 port. Make sure no other application is using this port. + +### Run the sample + +First, you need to start the server-side application of this sample. + +On the [client-credentials-flow-server](https://github.com/seedstack/samples/tree/master/addons/oauth/client-credentials-flow-server) maven project, run the main class **org.seedstack.samples.oauth.StartMeFirst** . + +Then, in parallel, run the **org.seedstack.samples.oauth.StartInSecond** of this maven project. + +As it is machine to machine there is no user interaction, in the client logs, you should see the different steps of +the process and finaly the account ID displayed after the log "Secured call response". \ No newline at end of file diff --git a/addons/oauth/client-credentials-flow-cli/pom.xml b/addons/oauth/client-credentials-flow-cli/pom.xml new file mode 100644 index 0000000..6b4567a --- /dev/null +++ b/addons/oauth/client-credentials-flow-cli/pom.xml @@ -0,0 +1,61 @@ + + + + 4.0.0 + + + org.seedstack.samples + samples + 20.11-SNAPSHOT + ../../../pom.xml + + + client-credentials-flow-cli + + + + org.seedstack.seed + seed-cli + + + org.seedstack.addons.feign + feign + + + org.seedstack.addons.oauth + oauth + + + ch.qos.logback + logback-classic + ${logback.version} + + + + org.seedstack.seed + seed-testing-junit4 + test + + + org.assertj + assertj-core + ${assertj.version} + test + + + io.rest-assured + rest-assured + ${rest-assured.version} + test + + + diff --git a/addons/oauth/client-credentials-flow-cli/src/main/java/org/seedstack/samples/oauth/AccountInfoCommandLineHandler.java b/addons/oauth/client-credentials-flow-cli/src/main/java/org/seedstack/samples/oauth/AccountInfoCommandLineHandler.java new file mode 100644 index 0000000..103b0df --- /dev/null +++ b/addons/oauth/client-credentials-flow-cli/src/main/java/org/seedstack/samples/oauth/AccountInfoCommandLineHandler.java @@ -0,0 +1,76 @@ +/* + * Copyright © 2013-2020, The SeedStack authors + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.seedstack.samples.oauth; + +import org.seedstack.oauth.OAuthAuthenticationToken; +import org.seedstack.oauth.OAuthService; +import org.seedstack.samples.oauth.api.AccountInfo; +import org.seedstack.samples.oauth.api.SecuredApi; +import org.seedstack.samples.oauth.error.OAuthCredentialErrorCode; +import org.seedstack.samples.oauth.error.OAuthSampleException; +import org.seedstack.seed.Logging; +import org.seedstack.seed.SeedException; +import org.seedstack.seed.cli.CliCommand; +import org.seedstack.seed.cli.CommandLineHandler; +import org.slf4j.Logger; + +import javax.inject.Inject; + +@CliCommand("accountInfo") +public class AccountInfoCommandLineHandler implements CommandLineHandler { + + @Logging + private Logger logger; + + @Inject + private OAuthService oAuthService; + + @Inject + private SecuredApi securedApi; + + @Override + public Integer call() throws Exception { + logger.info("OAuth samples - Client credential flow - Client started"); + + //First step - Request for authentication token + OAuthAuthenticationToken token = requestAuthenticationToken(); + + //Step 2 - Request secured api with bearer token + String accountId = callSecuredApi(token).getAccountId(); + + logger.info("Secured call response : {}", accountId); + return 0; + } + + /** + * Request the Authentication token from the Auth server + * Uses the Oauth configuration, more more information to set here. + * + * @return OAuthAuthenticationToken Authentication token + */ + private OAuthAuthenticationToken requestAuthenticationToken() { + logger.info("Requesting authentication token."); + OAuthAuthenticationToken token = oAuthService.requestTokensWithClientCredentials(); + if (token == null) { + throw SeedException.wrap(new OAuthSampleException("Authentication token is null"), OAuthCredentialErrorCode.TOKEN_NOT_RECEIVED); + } + logger.info("Authentication token received."); + return token; + } + + /** + * Call the secured API with an authentication token + * + * @param token The authentication token previously requested from Auth server + * @return AccountInfo the account information response + */ + private AccountInfo callSecuredApi(OAuthAuthenticationToken token) { + logger.info("Calling secured web service"); + return securedApi.accountInfo(token.getAccessToken()); + } +} \ No newline at end of file diff --git a/addons/oauth/client-credentials-flow-cli/src/main/java/org/seedstack/samples/oauth/StartInSecond.java b/addons/oauth/client-credentials-flow-cli/src/main/java/org/seedstack/samples/oauth/StartInSecond.java new file mode 100644 index 0000000..e4bf040 --- /dev/null +++ b/addons/oauth/client-credentials-flow-cli/src/main/java/org/seedstack/samples/oauth/StartInSecond.java @@ -0,0 +1,22 @@ +/* + * Copyright © 2013-2020, The SeedStack authors + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.seedstack.samples.oauth; + +import org.seedstack.seed.core.Seed; + +/** + * OAuth sample - client credential flow + * Run this class once the Authentication server is running and the client-credential-flow-server secured web service has been started. + * (Class StartMeFirst) + */ +public class StartInSecond { + + public static void main(String[] args) throws Exception { + Seed.getLauncher().launch(new String[]{"accountInfo"}); + } +} diff --git a/addons/oauth/client-credentials-flow-cli/src/main/java/org/seedstack/samples/oauth/api/AccountInfo.java b/addons/oauth/client-credentials-flow-cli/src/main/java/org/seedstack/samples/oauth/api/AccountInfo.java new file mode 100644 index 0000000..1c38f4e --- /dev/null +++ b/addons/oauth/client-credentials-flow-cli/src/main/java/org/seedstack/samples/oauth/api/AccountInfo.java @@ -0,0 +1,23 @@ +/* + * Copyright © 2013-2020, The SeedStack authors + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.seedstack.samples.oauth.api; + +/** + * Maps the information retrieved from the protected resource - Only need the account ID + */ +public class AccountInfo { + String accountId; + + public void setAccountId(String accountId) { + this.accountId = accountId; + } + + public String getAccountId() { + return accountId; + } +} diff --git a/addons/oauth/client-credentials-flow-cli/src/main/java/org/seedstack/samples/oauth/api/SecuredApi.java b/addons/oauth/client-credentials-flow-cli/src/main/java/org/seedstack/samples/oauth/api/SecuredApi.java new file mode 100644 index 0000000..9f09911 --- /dev/null +++ b/addons/oauth/client-credentials-flow-cli/src/main/java/org/seedstack/samples/oauth/api/SecuredApi.java @@ -0,0 +1,26 @@ +/* + * Copyright © 2013-2020, The SeedStack authors + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.seedstack.samples.oauth.api; + +import feign.Headers; +import feign.Param; +import feign.RequestLine; +import org.seedstack.feign.FeignApi; + +/** + * Feign descriptor for the secured API + */ +@FeignApi +public interface SecuredApi { + + /**Request for the accountInfo secured api on the distant server*/ + @RequestLine("GET /accountInfo") + @Headers("Authorization: Bearer {accessToken}") + AccountInfo accountInfo(@Param("accessToken") String accessToken); + +} diff --git a/addons/oauth/client-credentials-flow-cli/src/main/java/org/seedstack/samples/oauth/error/OAuthCredentialErrorCode.java b/addons/oauth/client-credentials-flow-cli/src/main/java/org/seedstack/samples/oauth/error/OAuthCredentialErrorCode.java new file mode 100644 index 0000000..38e0764 --- /dev/null +++ b/addons/oauth/client-credentials-flow-cli/src/main/java/org/seedstack/samples/oauth/error/OAuthCredentialErrorCode.java @@ -0,0 +1,18 @@ +/* + * Copyright © 2013-2020, The SeedStack authors + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.seedstack.samples.oauth.error; + +import org.seedstack.shed.exception.ErrorCode; + +/** + * OAuth Credential Flow errors codes + */ +public enum OAuthCredentialErrorCode implements ErrorCode { + TOKEN_NOT_RECEIVED, + REQUEST_NOT_SUCCESSFUL +} diff --git a/addons/oauth/client-credentials-flow-cli/src/main/java/org/seedstack/samples/oauth/error/OAuthSampleException.java b/addons/oauth/client-credentials-flow-cli/src/main/java/org/seedstack/samples/oauth/error/OAuthSampleException.java new file mode 100644 index 0000000..d57ae17 --- /dev/null +++ b/addons/oauth/client-credentials-flow-cli/src/main/java/org/seedstack/samples/oauth/error/OAuthSampleException.java @@ -0,0 +1,21 @@ +/* + * Copyright © 2013-2020, The SeedStack authors + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.seedstack.samples.oauth.error; + +/** + * Exception related to Oauth addon sample + */ +public class OAuthSampleException extends Exception{ + /** + * OAuthSampleException constructor + * @param message The exception message + */ + public OAuthSampleException(String message){ + super(message); + } +} diff --git a/addons/oauth/client-credentials-flow-cli/src/main/resources/META-INF/errors/org.seedstack.samples.oauth.error.OAuthCredentialErrorCode.properties b/addons/oauth/client-credentials-flow-cli/src/main/resources/META-INF/errors/org.seedstack.samples.oauth.error.OAuthCredentialErrorCode.properties new file mode 100644 index 0000000..6594b10 --- /dev/null +++ b/addons/oauth/client-credentials-flow-cli/src/main/resources/META-INF/errors/org.seedstack.samples.oauth.error.OAuthCredentialErrorCode.properties @@ -0,0 +1,4 @@ +TOKEN_NOT_RECEIVED.message= The Authentication Token was not received +TOKEN_NOT_RECEIVED.fix= Check that the Oauth samples authentication server is running. +REQUEST_NOT_SUCCESSFUL.message= The secured api wasn't requested successfully +REQUEST_NOT_SUCCESSFUL.fix= Check that the oauth sample credential flow server if running. Start class StartMeFirst. \ No newline at end of file diff --git a/addons/oauth/client-credentials-flow-cli/src/main/resources/application.yaml b/addons/oauth/client-credentials-flow-cli/src/main/resources/application.yaml new file mode 100644 index 0000000..fc1e036 --- /dev/null +++ b/addons/oauth/client-credentials-flow-cli/src/main/resources/application.yaml @@ -0,0 +1,29 @@ +# +# Copyright © 2013-2020, The SeedStack authors +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# + +logging: + level: INFO + +application: + id: oauth-sample + +#Using feign addon to call the secured API +feign: + endpoints: + org.seedstack.samples.oauth.api.SecuredApi: + baseUrl: http://localhost:8090/api + decoder: feign.jackson.JacksonDecoder + +# Oauth configuration, only requires clientID & client secret +# The authentication server must have been configured to accept credential flow from this client +security: + realms: OAuthRealm + oauth: + discoveryDocument: http://localhost:8080/auth/realms/SeedSamplesRealm/.well-known/openid-configuration + clientId: SeedStackClientCredentials + clientSecret: 8b8d20f8-281d-41b4-84ef-be9418b4a21c \ No newline at end of file diff --git a/addons/oauth/client-credentials-flow-server/README.md b/addons/oauth/client-credentials-flow-server/README.md new file mode 100644 index 0000000..bc404dc --- /dev/null +++ b/addons/oauth/client-credentials-flow-server/README.md @@ -0,0 +1,5 @@ +# OAuth addon - Authorization with Client Credentials Flow + +This is the server side of the Oauth addon client credential flow. + +Please, consult [this documentation](https://github.com/seedstack/samples/tree/master/addons/oauth/client-credentials-flow-cli) for more details. \ No newline at end of file diff --git a/addons/oauth/client-credentials-flow-server/pom.xml b/addons/oauth/client-credentials-flow-server/pom.xml new file mode 100644 index 0000000..c2b072f --- /dev/null +++ b/addons/oauth/client-credentials-flow-server/pom.xml @@ -0,0 +1,65 @@ + + + + 4.0.0 + + + org.seedstack.samples + samples + 20.11-SNAPSHOT + ../../../pom.xml + + + client-credentials-flow-server + + + + org.seedstack.seed + seed-rest-jersey2 + + + org.seedstack.seed + seed-web-undertow + + + org.seedstack.seed + seed-web-security + + + org.seedstack.addons.oauth + oauth + + + ch.qos.logback + logback-classic + ${logback.version} + + + + org.seedstack.seed + seed-testing-junit4 + test + + + org.assertj + assertj-core + ${assertj.version} + test + + + io.rest-assured + rest-assured + ${rest-assured.version} + test + + + diff --git a/addons/oauth/client-credentials-flow-server/src/main/java/org/seedstack/samples/oauth/AccountInfoRepresentation.java b/addons/oauth/client-credentials-flow-server/src/main/java/org/seedstack/samples/oauth/AccountInfoRepresentation.java new file mode 100644 index 0000000..47d9449 --- /dev/null +++ b/addons/oauth/client-credentials-flow-server/src/main/java/org/seedstack/samples/oauth/AccountInfoRepresentation.java @@ -0,0 +1,24 @@ +/* + * Copyright © 2013-2020, The SeedStack authors + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.seedstack.samples.oauth; + +/** + * Representation class for api/accountInfo response + */ +public class AccountInfoRepresentation { + /**Account identifier*/ + private String accountId; + + public String getAccountId() { + return accountId; + } + + public void setAccountId(String accountId) { + this.accountId = accountId; + } +} diff --git a/addons/oauth/client-credentials-flow-server/src/main/java/org/seedstack/samples/oauth/AccountInfoResource.java b/addons/oauth/client-credentials-flow-server/src/main/java/org/seedstack/samples/oauth/AccountInfoResource.java new file mode 100644 index 0000000..ed94704 --- /dev/null +++ b/addons/oauth/client-credentials-flow-server/src/main/java/org/seedstack/samples/oauth/AccountInfoResource.java @@ -0,0 +1,55 @@ +/* + * Copyright © 2013-2020, The SeedStack authors + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.seedstack.samples.oauth; + +import org.seedstack.seed.Logging; +import org.seedstack.seed.security.RequiresPermissions; +import org.seedstack.seed.security.SecuritySupport; +import org.seedstack.seed.security.principals.Principals; +import org.seedstack.seed.security.principals.SimplePrincipalProvider; +import org.slf4j.Logger; + +import javax.inject.Inject; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import java.util.Optional; + +/** + * Secured api for account information + * security support is available, all interactions with Authentication server has + * already been made by the framework + */ +@Path("/accountInfo") +public class AccountInfoResource { + + /**Security information*/ + @Inject + private SecuritySupport securitySupport; + + @Logging + private Logger logger; + + /** + * The secured end point. + * Accessing this endpoint requires the "profile" permission + * At this point, security information such as Principals are known + * @return AccountInfoRepresentation + */ + @GET + @Produces("application/json") + @RequiresPermissions("profile") + public AccountInfoRepresentation getInfos(){ + logger.info("Request received and accepted."); + AccountInfoRepresentation representation = new AccountInfoRepresentation(); + Optional.ofNullable(securitySupport.getSimplePrincipalByName(Principals.IDENTITY)) + .map(SimplePrincipalProvider::get) + .ifPresent(representation::setAccountId); + return representation; + } +} diff --git a/full-apps/catalog-microservice/src/test/java/org/seedstack/samples/catalog/Demo.java b/addons/oauth/client-credentials-flow-server/src/main/java/org/seedstack/samples/oauth/StartMeFirst.java similarity index 64% rename from full-apps/catalog-microservice/src/test/java/org/seedstack/samples/catalog/Demo.java rename to addons/oauth/client-credentials-flow-server/src/main/java/org/seedstack/samples/oauth/StartMeFirst.java index f30a94c..709717b 100644 --- a/full-apps/catalog-microservice/src/test/java/org/seedstack/samples/catalog/Demo.java +++ b/addons/oauth/client-credentials-flow-server/src/main/java/org/seedstack/samples/oauth/StartMeFirst.java @@ -1,16 +1,18 @@ /* - * Copyright © 2013-2018, The SeedStack authors + * Copyright © 2013-2020, The SeedStack authors * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -package org.seedstack.samples.catalog; +package org.seedstack.samples.oauth; import org.seedstack.seed.core.Seed; -public class Demo { +/** + * Starts the web server, enabling the api/accountInfo secured request + */ +public class StartMeFirst { public static void main(String[] args) throws Exception { Seed.getLauncher().launch(args); } diff --git a/addons/oauth/client-credentials-flow-server/src/main/resources/application.yaml b/addons/oauth/client-credentials-flow-server/src/main/resources/application.yaml new file mode 100644 index 0000000..191a4af --- /dev/null +++ b/addons/oauth/client-credentials-flow-server/src/main/resources/application.yaml @@ -0,0 +1,36 @@ +# +# Copyright © 2013-2020, The SeedStack authors +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# + +logging: + level: INFO + +application: + id: oauth-sample + +#As the authentication server is running on port 8080, this sample runs on port 8090 +web: + server: + port: 8090 + +rest: + path: /api + +#Secure the api with Oauth +security: + realms: OAuthRealm + web: + urls: + - pattern: /api/** + filters: oauth + +#The request is having a token in its Authorization header +#This configuration is for the oauth addon to validate the token + oauth: + discoveryDocument: http://localhost:8080/auth/realms/SeedSamplesRealm/.well-known/openid-configuration + allowedAudiences: account + diff --git a/addons/oauth/deploy.sh b/addons/oauth/deploy.sh new file mode 100755 index 0000000..4391fb3 --- /dev/null +++ b/addons/oauth/deploy.sh @@ -0,0 +1,28 @@ +# +# Copyright © 2013-2020, The SeedStack authors +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# + +if [ $TRAVIS_PULL_REQUEST = "false" ] +then + echo "Not a PR => push" + if [ "$TRAVIS_TAG" != "" ] + then + echo "It's a tag" + docker tag sample-oauth-server:$COMMIT $DOCKER_REPO/sample-oauth-server:$TRAVIS_TAG + docker login -u $DOCKER_USERNAME -p $DOCKER_PASSWORD + docker push $DOCKER_REPO/sample-oauth-server:$TRAVIS_TAG + + else + echo "Not a tag" + export TAG=`if [ "$TRAVIS_BRANCH" == "master" ]; then echo "latest"; else echo $TRAVIS_BRANCH ; fi` + docker tag sample-oauth-server:$COMMIT $DOCKER_REPO/sample-oauth-server:$TAG + docker login -u $DOCKER_USERNAME -p $DOCKER_PASSWORD + docker push $DOCKER_REPO/sample-oauth-server:$TAG + fi +else + echo "PR => skip push" +fi diff --git a/addons/spring-batch/pom.xml b/addons/spring-batch/pom.xml index eebed00..309c4bc 100644 --- a/addons/spring-batch/pom.xml +++ b/addons/spring-batch/pom.xml @@ -15,8 +15,8 @@ org.seedstack.samples samples - 18.4 - ../.. + 20.11-SNAPSHOT + ../../pom.xml spring-batch-sample @@ -40,9 +40,8 @@ - org.seedstack - batch-composite - pom + org.seedstack.addons.spring + spring-bridge-batch org.springframework.batch diff --git a/addons/spring-bridge/pom.xml b/addons/spring-bridge/pom.xml index f827cb9..2167f42 100644 --- a/addons/spring-bridge/pom.xml +++ b/addons/spring-bridge/pom.xml @@ -15,8 +15,8 @@ org.seedstack.samples samples - 18.4 - ../.. + 20.11-SNAPSHOT + ../../pom.xml spring-bridge-sample diff --git a/addons/w20-bridge/pom.xml b/addons/w20-bridge/pom.xml index 5266eee..d98697c 100644 --- a/addons/w20-bridge/pom.xml +++ b/addons/w20-bridge/pom.xml @@ -15,8 +15,8 @@ org.seedstack.samples samples - 18.4 - ../.. + 20.11-SNAPSHOT + ../../pom.xml w20-bridge-sample diff --git a/addons/web-services/pom.xml b/addons/web-services/pom.xml index e51f532..c8975ac 100644 --- a/addons/web-services/pom.xml +++ b/addons/web-services/pom.xml @@ -15,8 +15,8 @@ org.seedstack.samples samples - 18.4 - ../.. + 20.11-SNAPSHOT + ../../pom.xml web-services-sample @@ -24,9 +24,9 @@ - org.codehaus.mojo + com.sun.xml.ws jaxws-maven-plugin - 2.5 + 2.3.3 generate-product-info @@ -85,12 +85,6 @@ logback-classic ${logback.version} - - - com.google.code.findbugs - jsr305 - 3.0.2 - org.seedstack.seed @@ -104,49 +98,4 @@ test - - - - win - - - windows - - - - ${java.home}/bin/wsimport.exe - - - - nix - - - !windows - - - - ${java.home}/bin/wsimport - - - - jdk9-wsimport - - 9 - - - - - - org.codehaus.mojo - jaxws-maven-plugin - 2.5 - - ${tool.wsimport} - - - - - - - diff --git a/addons/web-services/src/main/resources/application.yaml b/addons/web-services/src/main/resources/application.yaml index cfcfb1f..e9e53c3 100644 --- a/addons/web-services/src/main/resources/application.yaml +++ b/addons/web-services/src/main/resources/application.yaml @@ -6,10 +6,6 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. # -web: - server: - port: ${env.PORT} - security: users: demo: demo diff --git a/addons/web-services/src/test/java/org/seedstack/samples/ws/WebServiceIT.java b/addons/web-services/src/test/java/org/seedstack/samples/ws/WebServiceIT.java index f22351f..ac6f8b9 100644 --- a/addons/web-services/src/test/java/org/seedstack/samples/ws/WebServiceIT.java +++ b/addons/web-services/src/test/java/org/seedstack/samples/ws/WebServiceIT.java @@ -31,14 +31,14 @@ public class WebServiceIT { private CalculatorService calculatorService; @Inject private ProductInfoService productInfoService; - @Configuration("web.runtime.baseUrl") + @Configuration("runtime.web.baseUrl") private String baseUrl; @Test public void testSimple() throws Exception { ProductInfoPortType productInfoPort = productInfoService.getProductInfoPort(); ((BindingProvider) productInfoPort).getRequestContext() - .put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, baseUrl + "product-info"); + .put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, baseUrl + "/product-info"); assertThat(productInfoPort.productInfoById(1).getDesignation()).isEqualTo("Product #1"); } @@ -47,7 +47,7 @@ public void testSimple() throws Exception { public void testSimpleWithException() throws Exception { ProductInfoPortType productInfoPort = productInfoService.getProductInfoPort(); ((BindingProvider) productInfoPort).getRequestContext() - .put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, baseUrl + "product-info"); + .put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, baseUrl + "/product-info"); productInfoPort.productInfoById(-1); @@ -58,7 +58,7 @@ public void testSimpleWithException() throws Exception { public void testUsernameToken() throws Exception { CalculatorPortType calculatorPort = calculatorService.getCalculatorUsernameTokenPort(); ((BindingProvider) calculatorPort).getRequestContext() - .put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, baseUrl + "calculator-username-token"); + .put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, baseUrl + "/calculator-username-token"); // User name and password below will be used for username token policy. // You can omit them if you have a custom handler configured in the wsit-client.xml file. ((BindingProvider) calculatorPort).getRequestContext().put(BindingProvider.USERNAME_PROPERTY, "demo"); @@ -71,7 +71,7 @@ public void testUsernameToken() throws Exception { public void testCertificate() throws Exception { CalculatorPortType calculatorPort = calculatorService.getCalculatorCertificatePort(); ((BindingProvider) calculatorPort).getRequestContext() - .put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, baseUrl + "calculator-certificate"); + .put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, baseUrl + "/calculator-certificate"); assertThat(calculatorPort.add(1, 1)).isEqualTo(2); } diff --git a/basics/README.md b/basics/README.md index 21e6d28..91080f8 100644 --- a/basics/README.md +++ b/basics/README.md @@ -15,5 +15,6 @@ test class(es). | Plugin | [plugin](https://github.com/seedstack/samples/tree/master/basics/plugin) | Extending SeedStack with plugins | | REST | [rest](https://github.com/seedstack/samples/tree/master/basics/rest) | JAX-RS and hypermedia | | Security | [security](https://github.com/seedstack/samples/tree/master/basics/security) | Application security | -| Web | [web](https://github.com/seedstack/samples/tree/master/basics/web) | Web Servlets, Filters and Listeners | +| Servlet | [servlet](https://github.com/seedstack/samples/tree/master/basics/servlet) | Web Servlets, Filters and Listeners | +| Undertow | [undertow](https://github.com/seedstack/samples/tree/master/basics/undertow) | Undertow, HTTPS, SSL/TLS | | WebSocket | [websocket](https://github.com/seedstack/samples/tree/master/basics/websocket) | WebSockets | diff --git a/basics/business/pom.xml b/basics/business/pom.xml index 1c328d3..cbde328 100644 --- a/basics/business/pom.xml +++ b/basics/business/pom.xml @@ -15,8 +15,8 @@ org.seedstack.samples samples - 18.4 - ../.. + 20.11-SNAPSHOT + ../../pom.xml business-sample diff --git a/basics/cli/pom.xml b/basics/cli/pom.xml index 695a012..78a961f 100644 --- a/basics/cli/pom.xml +++ b/basics/cli/pom.xml @@ -15,8 +15,8 @@ org.seedstack.samples samples - 18.4 - ../.. + 20.11-SNAPSHOT + ../../pom.xml cli-sample diff --git a/basics/configuration/pom.xml b/basics/configuration/pom.xml index 7a5d7ec..5314ba6 100644 --- a/basics/configuration/pom.xml +++ b/basics/configuration/pom.xml @@ -15,8 +15,8 @@ org.seedstack.samples samples - 18.4 - ../.. + 20.11-SNAPSHOT + ../../pom.xml configuration-sample diff --git a/basics/configuration/src/test/java/org/seedstack/samples/ConfigurationIT.java b/basics/configuration/src/test/java/org/seedstack/samples/ConfigurationIT.java index ba80574..eb77d3c 100644 --- a/basics/configuration/src/test/java/org/seedstack/samples/ConfigurationIT.java +++ b/basics/configuration/src/test/java/org/seedstack/samples/ConfigurationIT.java @@ -10,6 +10,7 @@ import static org.assertj.core.api.Assertions.assertThat; +import java.util.Optional; import javax.inject.Inject; import org.junit.Test; import org.junit.runner.RunWith; @@ -44,7 +45,7 @@ public void testBasicConfiguration() { @Test public void testEnvironmentVariable() { - assertThat(environmentVariable).isEqualTo(System.getenv("JAVA_HOME")); + assertThat(environmentVariable).isEqualTo(Optional.ofNullable(System.getenv("JAVA_HOME")).orElse("")); } @Test diff --git a/basics/diagnostic/pom.xml b/basics/diagnostic/pom.xml index e81e695..4e7e34f 100644 --- a/basics/diagnostic/pom.xml +++ b/basics/diagnostic/pom.xml @@ -15,8 +15,8 @@ org.seedstack.samples samples - 18.4 - ../.. + 20.11-SNAPSHOT + ../../pom.xml diagnostic-sample diff --git a/basics/guice/pom.xml b/basics/guice/pom.xml index 28f2774..60e5962 100644 --- a/basics/guice/pom.xml +++ b/basics/guice/pom.xml @@ -15,8 +15,8 @@ org.seedstack.samples samples - 18.4 - ../.. + 20.11-SNAPSHOT + ../../pom.xml guice-sample diff --git a/basics/plugin/pom.xml b/basics/plugin/pom.xml index 70dfd8f..d352d7d 100644 --- a/basics/plugin/pom.xml +++ b/basics/plugin/pom.xml @@ -15,8 +15,8 @@ org.seedstack.samples samples - 18.4 - ../.. + 20.11-SNAPSHOT + ../../pom.xml plugin-sample diff --git a/basics/rest/pom.xml b/basics/rest/pom.xml index 426a24a..08bb152 100644 --- a/basics/rest/pom.xml +++ b/basics/rest/pom.xml @@ -15,8 +15,8 @@ org.seedstack.samples samples - 18.4 - ../.. + 20.11-SNAPSHOT + ../../pom.xml rest-sample diff --git a/basics/rest/src/test/java/org/seedstack/samples/rest/RestIT.java b/basics/rest/src/test/java/org/seedstack/samples/rest/RestIT.java index 998972a..ac0cd5b 100644 --- a/basics/rest/src/test/java/org/seedstack/samples/rest/RestIT.java +++ b/basics/rest/src/test/java/org/seedstack/samples/rest/RestIT.java @@ -20,7 +20,7 @@ @RunWith(JUnit4Runner.class) @LaunchWithUndertow public class RestIT { - @Configuration("web.runtime.baseUrl") + @Configuration("runtime.web.baseUrl") private String baseUrl; @Test @@ -29,6 +29,6 @@ public void testGreeter() { .statusCode(200) .body(equalTo("Hello World from rest-sample!")) .when() - .get(baseUrl + "greeter/World"); + .get(baseUrl + "/greeter/World"); } } diff --git a/basics/security/pom.xml b/basics/security/pom.xml index 4cf047a..0b34a78 100644 --- a/basics/security/pom.xml +++ b/basics/security/pom.xml @@ -15,8 +15,8 @@ org.seedstack.samples samples - 18.4 - ../.. + 20.11-SNAPSHOT + ../../pom.xml security-sample diff --git a/basics/servlet/LICENSE b/basics/servlet/LICENSE new file mode 100644 index 0000000..a612ad9 --- /dev/null +++ b/basics/servlet/LICENSE @@ -0,0 +1,373 @@ +Mozilla Public License Version 2.0 +================================== + +1. Definitions +-------------- + +1.1. "Contributor" + means each individual or legal entity that creates, contributes to + the creation of, or owns Covered Software. + +1.2. "Contributor Version" + means the combination of the Contributions of others (if any) used + by a Contributor and that particular Contributor's Contribution. + +1.3. "Contribution" + means Covered Software of a particular Contributor. + +1.4. "Covered Software" + means Source Code Form to which the initial Contributor has attached + the notice in Exhibit A, the Executable Form of such Source Code + Form, and Modifications of such Source Code Form, in each case + including portions thereof. + +1.5. "Incompatible With Secondary Licenses" + means + + (a) that the initial Contributor has attached the notice described + in Exhibit B to the Covered Software; or + + (b) that the Covered Software was made available under the terms of + version 1.1 or earlier of the License, but not also under the + terms of a Secondary License. + +1.6. "Executable Form" + means any form of the work other than Source Code Form. + +1.7. "Larger Work" + means a work that combines Covered Software with other material, in + a separate file or files, that is not Covered Software. + +1.8. "License" + means this document. + +1.9. "Licensable" + means having the right to grant, to the maximum extent possible, + whether at the time of the initial grant or subsequently, any and + all of the rights conveyed by this License. + +1.10. "Modifications" + means any of the following: + + (a) any file in Source Code Form that results from an addition to, + deletion from, or modification of the contents of Covered + Software; or + + (b) any new file in Source Code Form that contains any Covered + Software. + +1.11. "Patent Claims" of a Contributor + means any patent claim(s), including without limitation, method, + process, and apparatus claims, in any patent Licensable by such + Contributor that would be infringed, but for the grant of the + License, by the making, using, selling, offering for sale, having + made, import, or transfer of either its Contributions or its + Contributor Version. + +1.12. "Secondary License" + means either the GNU General Public License, Version 2.0, the GNU + Lesser General Public License, Version 2.1, the GNU Affero General + Public License, Version 3.0, or any later versions of those + licenses. + +1.13. "Source Code Form" + means the form of the work preferred for making modifications. + +1.14. "You" (or "Your") + means an individual or a legal entity exercising rights under this + License. For legal entities, "You" includes any entity that + controls, is controlled by, or is under common control with You. For + purposes of this definition, "control" means (a) the power, direct + or indirect, to cause the direction or management of such entity, + whether by contract or otherwise, or (b) ownership of more than + fifty percent (50%) of the outstanding shares or beneficial + ownership of such entity. + +2. License Grants and Conditions +-------------------------------- + +2.1. Grants + +Each Contributor hereby grants You a world-wide, royalty-free, +non-exclusive license: + +(a) under intellectual property rights (other than patent or trademark) + Licensable by such Contributor to use, reproduce, make available, + modify, display, perform, distribute, and otherwise exploit its + Contributions, either on an unmodified basis, with Modifications, or + as part of a Larger Work; and + +(b) under Patent Claims of such Contributor to make, use, sell, offer + for sale, have made, import, and otherwise transfer either its + Contributions or its Contributor Version. + +2.2. Effective Date + +The licenses granted in Section 2.1 with respect to any Contribution +become effective for each Contribution on the date the Contributor first +distributes such Contribution. + +2.3. Limitations on Grant Scope + +The licenses granted in this Section 2 are the only rights granted under +this License. No additional rights or licenses will be implied from the +distribution or licensing of Covered Software under this License. +Notwithstanding Section 2.1(b) above, no patent license is granted by a +Contributor: + +(a) for any code that a Contributor has removed from Covered Software; + or + +(b) for infringements caused by: (i) Your and any other third party's + modifications of Covered Software, or (ii) the combination of its + Contributions with other software (except as part of its Contributor + Version); or + +(c) under Patent Claims infringed by Covered Software in the absence of + its Contributions. + +This License does not grant any rights in the trademarks, service marks, +or logos of any Contributor (except as may be necessary to comply with +the notice requirements in Section 3.4). + +2.4. Subsequent Licenses + +No Contributor makes additional grants as a result of Your choice to +distribute the Covered Software under a subsequent version of this +License (see Section 10.2) or under the terms of a Secondary License (if +permitted under the terms of Section 3.3). + +2.5. Representation + +Each Contributor represents that the Contributor believes its +Contributions are its original creation(s) or it has sufficient rights +to grant the rights to its Contributions conveyed by this License. + +2.6. Fair Use + +This License is not intended to limit any rights You have under +applicable copyright doctrines of fair use, fair dealing, or other +equivalents. + +2.7. Conditions + +Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted +in Section 2.1. + +3. Responsibilities +------------------- + +3.1. Distribution of Source Form + +All distribution of Covered Software in Source Code Form, including any +Modifications that You create or to which You contribute, must be under +the terms of this License. You must inform recipients that the Source +Code Form of the Covered Software is governed by the terms of this +License, and how they can obtain a copy of this License. You may not +attempt to alter or restrict the recipients' rights in the Source Code +Form. + +3.2. Distribution of Executable Form + +If You distribute Covered Software in Executable Form then: + +(a) such Covered Software must also be made available in Source Code + Form, as described in Section 3.1, and You must inform recipients of + the Executable Form how they can obtain a copy of such Source Code + Form by reasonable means in a timely manner, at a charge no more + than the cost of distribution to the recipient; and + +(b) You may distribute such Executable Form under the terms of this + License, or sublicense it under different terms, provided that the + license for the Executable Form does not attempt to limit or alter + the recipients' rights in the Source Code Form under this License. + +3.3. Distribution of a Larger Work + +You may create and distribute a Larger Work under terms of Your choice, +provided that You also comply with the requirements of this License for +the Covered Software. If the Larger Work is a combination of Covered +Software with a work governed by one or more Secondary Licenses, and the +Covered Software is not Incompatible With Secondary Licenses, this +License permits You to additionally distribute such Covered Software +under the terms of such Secondary License(s), so that the recipient of +the Larger Work may, at their option, further distribute the Covered +Software under the terms of either this License or such Secondary +License(s). + +3.4. Notices + +You may not remove or alter the substance of any license notices +(including copyright notices, patent notices, disclaimers of warranty, +or limitations of liability) contained within the Source Code Form of +the Covered Software, except that You may alter any license notices to +the extent required to remedy known factual inaccuracies. + +3.5. Application of Additional Terms + +You may choose to offer, and to charge a fee for, warranty, support, +indemnity or liability obligations to one or more recipients of Covered +Software. However, You may do so only on Your own behalf, and not on +behalf of any Contributor. You must make it absolutely clear that any +such warranty, support, indemnity, or liability obligation is offered by +You alone, and You hereby agree to indemnify every Contributor for any +liability incurred by such Contributor as a result of warranty, support, +indemnity or liability terms You offer. You may include additional +disclaimers of warranty and limitations of liability specific to any +jurisdiction. + +4. Inability to Comply Due to Statute or Regulation +--------------------------------------------------- + +If it is impossible for You to comply with any of the terms of this +License with respect to some or all of the Covered Software due to +statute, judicial order, or regulation then You must: (a) comply with +the terms of this License to the maximum extent possible; and (b) +describe the limitations and the code they affect. Such description must +be placed in a text file included with all distributions of the Covered +Software under this License. Except to the extent prohibited by statute +or regulation, such description must be sufficiently detailed for a +recipient of ordinary skill to be able to understand it. + +5. Termination +-------------- + +5.1. The rights granted under this License will terminate automatically +if You fail to comply with any of its terms. However, if You become +compliant, then the rights granted under this License from a particular +Contributor are reinstated (a) provisionally, unless and until such +Contributor explicitly and finally terminates Your grants, and (b) on an +ongoing basis, if such Contributor fails to notify You of the +non-compliance by some reasonable means prior to 60 days after You have +come back into compliance. Moreover, Your grants from a particular +Contributor are reinstated on an ongoing basis if such Contributor +notifies You of the non-compliance by some reasonable means, this is the +first time You have received notice of non-compliance with this License +from such Contributor, and You become compliant prior to 30 days after +Your receipt of the notice. + +5.2. If You initiate litigation against any entity by asserting a patent +infringement claim (excluding declaratory judgment actions, +counter-claims, and cross-claims) alleging that a Contributor Version +directly or indirectly infringes any patent, then the rights granted to +You by any and all Contributors for the Covered Software under Section +2.1 of this License shall terminate. + +5.3. In the event of termination under Sections 5.1 or 5.2 above, all +end user license agreements (excluding distributors and resellers) which +have been validly granted by You or Your distributors under this License +prior to termination shall survive termination. + +************************************************************************ +* * +* 6. Disclaimer of Warranty * +* ------------------------- * +* * +* Covered Software is provided under this License on an "as is" * +* basis, without warranty of any kind, either expressed, implied, or * +* statutory, including, without limitation, warranties that the * +* Covered Software is free of defects, merchantable, fit for a * +* particular purpose or non-infringing. The entire risk as to the * +* quality and performance of the Covered Software is with You. * +* Should any Covered Software prove defective in any respect, You * +* (not any Contributor) assume the cost of any necessary servicing, * +* repair, or correction. This disclaimer of warranty constitutes an * +* essential part of this License. No use of any Covered Software is * +* authorized under this License except under this disclaimer. * +* * +************************************************************************ + +************************************************************************ +* * +* 7. Limitation of Liability * +* -------------------------- * +* * +* Under no circumstances and under no legal theory, whether tort * +* (including negligence), contract, or otherwise, shall any * +* Contributor, or anyone who distributes Covered Software as * +* permitted above, be liable to You for any direct, indirect, * +* special, incidental, or consequential damages of any character * +* including, without limitation, damages for lost profits, loss of * +* goodwill, work stoppage, computer failure or malfunction, or any * +* and all other commercial damages or losses, even if such party * +* shall have been informed of the possibility of such damages. This * +* limitation of liability shall not apply to liability for death or * +* personal injury resulting from such party's negligence to the * +* extent applicable law prohibits such limitation. Some * +* jurisdictions do not allow the exclusion or limitation of * +* incidental or consequential damages, so this exclusion and * +* limitation may not apply to You. * +* * +************************************************************************ + +8. Litigation +------------- + +Any litigation relating to this License may be brought only in the +courts of a jurisdiction where the defendant maintains its principal +place of business and such litigation shall be governed by laws of that +jurisdiction, without reference to its conflict-of-law provisions. +Nothing in this Section shall prevent a party's ability to bring +cross-claims or counter-claims. + +9. Miscellaneous +---------------- + +This License represents the complete agreement concerning the subject +matter hereof. If any provision of this License is held to be +unenforceable, such provision shall be reformed only to the extent +necessary to make it enforceable. Any law or regulation which provides +that the language of a contract shall be construed against the drafter +shall not be used to construe this License against a Contributor. + +10. Versions of the License +--------------------------- + +10.1. New Versions + +Mozilla Foundation is the license steward. Except as provided in Section +10.3, no one other than the license steward has the right to modify or +publish new versions of this License. Each version will be given a +distinguishing version number. + +10.2. Effect of New Versions + +You may distribute the Covered Software under the terms of the version +of the License under which You originally received the Covered Software, +or under the terms of any subsequent version published by the license +steward. + +10.3. Modified Versions + +If you create software not governed by this License, and you want to +create a new license for such software, you may create and use a +modified version of this License if you rename the license and remove +any references to the name of the license steward (except to note that +such modified license differs from this License). + +10.4. Distributing Source Code Form that is Incompatible With Secondary +Licenses + +If You choose to distribute Source Code Form that is Incompatible With +Secondary Licenses under the terms of this version of the License, the +notice described in Exhibit B of this License must be attached. + +Exhibit A - Source Code Form License Notice +------------------------------------------- + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. + +If it is not possible or desirable to put the notice in a particular +file, then You may include the notice in a location (such as a LICENSE +file in a relevant directory) where a recipient would be likely to look +for such a notice. + +You may add additional accurate notices of copyright ownership. + +Exhibit B - "Incompatible With Secondary Licenses" Notice +--------------------------------------------------------- + + This Source Code Form is "Incompatible With Secondary Licenses", as + defined by the Mozilla Public License, v. 2.0. diff --git a/basics/web/README.md b/basics/servlet/README.md similarity index 95% rename from basics/web/README.md rename to basics/servlet/README.md index ed71b55..e819eb1 100644 --- a/basics/web/README.md +++ b/basics/servlet/README.md @@ -1,22 +1,22 @@ -# SeedStack Web sample - -This sample demonstrates how to use the Servlet API with SeedStack. - -## Build - -```bash -mvn clean package -``` - -## Run - -Execute tests located in `src/test/java` in your IDE, or with Maven: - -```bash -mvn clean verify -``` - -## Copyright and license - -This source code is copyrighted by [The SeedStack Authors](https://github.com/seedstack/seedstack/blob/master/AUTHORS) and -released under the terms of the [Mozilla Public License 2.0](https://www.mozilla.org/MPL/2.0/). +# SeedStack Web sample + +This sample demonstrates how to use the Servlet API with SeedStack. + +## Build + +```bash +mvn clean package +``` + +## Run + +Execute tests located in `src/test/java` in your IDE, or with Maven: + +```bash +mvn clean verify +``` + +## Copyright and license + +This source code is copyrighted by [The SeedStack Authors](https://github.com/seedstack/seedstack/blob/master/AUTHORS) and +released under the terms of the [Mozilla Public License 2.0](https://www.mozilla.org/MPL/2.0/). diff --git a/basics/web/pom.xml b/basics/servlet/pom.xml similarity index 93% rename from basics/web/pom.xml rename to basics/servlet/pom.xml index d2f112c..239cd44 100644 --- a/basics/web/pom.xml +++ b/basics/servlet/pom.xml @@ -15,11 +15,11 @@ org.seedstack.samples samples - 18.4 - ../.. + 20.11-SNAPSHOT + ../../pom.xml - web-sample + servlet-sample diff --git a/basics/web/src/main/java/org/seedstack/samples/web/MyServlet.java b/basics/servlet/src/main/java/org/seedstack/samples/servlet/MyServlet.java similarity index 96% rename from basics/web/src/main/java/org/seedstack/samples/web/MyServlet.java rename to basics/servlet/src/main/java/org/seedstack/samples/servlet/MyServlet.java index 69006bf..bffa2a9 100644 --- a/basics/web/src/main/java/org/seedstack/samples/web/MyServlet.java +++ b/basics/servlet/src/main/java/org/seedstack/samples/servlet/MyServlet.java @@ -6,7 +6,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -package org.seedstack.samples.web; +package org.seedstack.samples.servlet; import java.io.IOException; import javax.inject.Inject; diff --git a/basics/servlet/src/main/resources/META-INF/resources/index.html b/basics/servlet/src/main/resources/META-INF/resources/index.html new file mode 100644 index 0000000..8841a10 --- /dev/null +++ b/basics/servlet/src/main/resources/META-INF/resources/index.html @@ -0,0 +1,11 @@ + + + + + Homepage + + +

Hello World!

+

This is automatically served at the root of the Web application.

+ + diff --git a/basics/web/src/test/java/org/seedstack/samples/web/WebIT.java b/basics/servlet/src/test/java/org/seedstack/samples/servlet/WebIT.java similarity index 86% rename from basics/web/src/test/java/org/seedstack/samples/web/WebIT.java rename to basics/servlet/src/test/java/org/seedstack/samples/servlet/WebIT.java index e29c78c..6e0ba03 100644 --- a/basics/web/src/test/java/org/seedstack/samples/web/WebIT.java +++ b/basics/servlet/src/test/java/org/seedstack/samples/servlet/WebIT.java @@ -6,7 +6,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -package org.seedstack.samples.web; +package org.seedstack.samples.servlet; import static io.restassured.RestAssured.expect; @@ -20,7 +20,7 @@ @RunWith(JUnit4Runner.class) @LaunchWithUndertow public class WebIT { - @Configuration("web.runtime.baseUrl") + @Configuration("runtime.web.baseUrl") private String baseUrl; @Test @@ -29,6 +29,6 @@ public void testServlet() { .statusCode(200) .body(Matchers.equalTo("Hello World from web-sample!")) .when() - .get(baseUrl + "my-servlet?name=World"); + .get(baseUrl + "/my-servlet?name=World"); } } diff --git a/basics/web/src/test/resources/application.yaml b/basics/servlet/src/test/resources/application.yaml similarity index 97% rename from basics/web/src/test/resources/application.yaml rename to basics/servlet/src/test/resources/application.yaml index c36b290..7d0b752 100644 --- a/basics/web/src/test/resources/application.yaml +++ b/basics/servlet/src/test/resources/application.yaml @@ -1,10 +1,10 @@ -# -# Copyright © 2013-2018, The SeedStack authors -# -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. -# - -application: +# +# Copyright © 2013-2018, The SeedStack authors +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# + +application: id: web-sample \ No newline at end of file diff --git a/basics/undertow/LICENSE b/basics/undertow/LICENSE new file mode 100644 index 0000000..a612ad9 --- /dev/null +++ b/basics/undertow/LICENSE @@ -0,0 +1,373 @@ +Mozilla Public License Version 2.0 +================================== + +1. Definitions +-------------- + +1.1. "Contributor" + means each individual or legal entity that creates, contributes to + the creation of, or owns Covered Software. + +1.2. "Contributor Version" + means the combination of the Contributions of others (if any) used + by a Contributor and that particular Contributor's Contribution. + +1.3. "Contribution" + means Covered Software of a particular Contributor. + +1.4. "Covered Software" + means Source Code Form to which the initial Contributor has attached + the notice in Exhibit A, the Executable Form of such Source Code + Form, and Modifications of such Source Code Form, in each case + including portions thereof. + +1.5. "Incompatible With Secondary Licenses" + means + + (a) that the initial Contributor has attached the notice described + in Exhibit B to the Covered Software; or + + (b) that the Covered Software was made available under the terms of + version 1.1 or earlier of the License, but not also under the + terms of a Secondary License. + +1.6. "Executable Form" + means any form of the work other than Source Code Form. + +1.7. "Larger Work" + means a work that combines Covered Software with other material, in + a separate file or files, that is not Covered Software. + +1.8. "License" + means this document. + +1.9. "Licensable" + means having the right to grant, to the maximum extent possible, + whether at the time of the initial grant or subsequently, any and + all of the rights conveyed by this License. + +1.10. "Modifications" + means any of the following: + + (a) any file in Source Code Form that results from an addition to, + deletion from, or modification of the contents of Covered + Software; or + + (b) any new file in Source Code Form that contains any Covered + Software. + +1.11. "Patent Claims" of a Contributor + means any patent claim(s), including without limitation, method, + process, and apparatus claims, in any patent Licensable by such + Contributor that would be infringed, but for the grant of the + License, by the making, using, selling, offering for sale, having + made, import, or transfer of either its Contributions or its + Contributor Version. + +1.12. "Secondary License" + means either the GNU General Public License, Version 2.0, the GNU + Lesser General Public License, Version 2.1, the GNU Affero General + Public License, Version 3.0, or any later versions of those + licenses. + +1.13. "Source Code Form" + means the form of the work preferred for making modifications. + +1.14. "You" (or "Your") + means an individual or a legal entity exercising rights under this + License. For legal entities, "You" includes any entity that + controls, is controlled by, or is under common control with You. For + purposes of this definition, "control" means (a) the power, direct + or indirect, to cause the direction or management of such entity, + whether by contract or otherwise, or (b) ownership of more than + fifty percent (50%) of the outstanding shares or beneficial + ownership of such entity. + +2. License Grants and Conditions +-------------------------------- + +2.1. Grants + +Each Contributor hereby grants You a world-wide, royalty-free, +non-exclusive license: + +(a) under intellectual property rights (other than patent or trademark) + Licensable by such Contributor to use, reproduce, make available, + modify, display, perform, distribute, and otherwise exploit its + Contributions, either on an unmodified basis, with Modifications, or + as part of a Larger Work; and + +(b) under Patent Claims of such Contributor to make, use, sell, offer + for sale, have made, import, and otherwise transfer either its + Contributions or its Contributor Version. + +2.2. Effective Date + +The licenses granted in Section 2.1 with respect to any Contribution +become effective for each Contribution on the date the Contributor first +distributes such Contribution. + +2.3. Limitations on Grant Scope + +The licenses granted in this Section 2 are the only rights granted under +this License. No additional rights or licenses will be implied from the +distribution or licensing of Covered Software under this License. +Notwithstanding Section 2.1(b) above, no patent license is granted by a +Contributor: + +(a) for any code that a Contributor has removed from Covered Software; + or + +(b) for infringements caused by: (i) Your and any other third party's + modifications of Covered Software, or (ii) the combination of its + Contributions with other software (except as part of its Contributor + Version); or + +(c) under Patent Claims infringed by Covered Software in the absence of + its Contributions. + +This License does not grant any rights in the trademarks, service marks, +or logos of any Contributor (except as may be necessary to comply with +the notice requirements in Section 3.4). + +2.4. Subsequent Licenses + +No Contributor makes additional grants as a result of Your choice to +distribute the Covered Software under a subsequent version of this +License (see Section 10.2) or under the terms of a Secondary License (if +permitted under the terms of Section 3.3). + +2.5. Representation + +Each Contributor represents that the Contributor believes its +Contributions are its original creation(s) or it has sufficient rights +to grant the rights to its Contributions conveyed by this License. + +2.6. Fair Use + +This License is not intended to limit any rights You have under +applicable copyright doctrines of fair use, fair dealing, or other +equivalents. + +2.7. Conditions + +Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted +in Section 2.1. + +3. Responsibilities +------------------- + +3.1. Distribution of Source Form + +All distribution of Covered Software in Source Code Form, including any +Modifications that You create or to which You contribute, must be under +the terms of this License. You must inform recipients that the Source +Code Form of the Covered Software is governed by the terms of this +License, and how they can obtain a copy of this License. You may not +attempt to alter or restrict the recipients' rights in the Source Code +Form. + +3.2. Distribution of Executable Form + +If You distribute Covered Software in Executable Form then: + +(a) such Covered Software must also be made available in Source Code + Form, as described in Section 3.1, and You must inform recipients of + the Executable Form how they can obtain a copy of such Source Code + Form by reasonable means in a timely manner, at a charge no more + than the cost of distribution to the recipient; and + +(b) You may distribute such Executable Form under the terms of this + License, or sublicense it under different terms, provided that the + license for the Executable Form does not attempt to limit or alter + the recipients' rights in the Source Code Form under this License. + +3.3. Distribution of a Larger Work + +You may create and distribute a Larger Work under terms of Your choice, +provided that You also comply with the requirements of this License for +the Covered Software. If the Larger Work is a combination of Covered +Software with a work governed by one or more Secondary Licenses, and the +Covered Software is not Incompatible With Secondary Licenses, this +License permits You to additionally distribute such Covered Software +under the terms of such Secondary License(s), so that the recipient of +the Larger Work may, at their option, further distribute the Covered +Software under the terms of either this License or such Secondary +License(s). + +3.4. Notices + +You may not remove or alter the substance of any license notices +(including copyright notices, patent notices, disclaimers of warranty, +or limitations of liability) contained within the Source Code Form of +the Covered Software, except that You may alter any license notices to +the extent required to remedy known factual inaccuracies. + +3.5. Application of Additional Terms + +You may choose to offer, and to charge a fee for, warranty, support, +indemnity or liability obligations to one or more recipients of Covered +Software. However, You may do so only on Your own behalf, and not on +behalf of any Contributor. You must make it absolutely clear that any +such warranty, support, indemnity, or liability obligation is offered by +You alone, and You hereby agree to indemnify every Contributor for any +liability incurred by such Contributor as a result of warranty, support, +indemnity or liability terms You offer. You may include additional +disclaimers of warranty and limitations of liability specific to any +jurisdiction. + +4. Inability to Comply Due to Statute or Regulation +--------------------------------------------------- + +If it is impossible for You to comply with any of the terms of this +License with respect to some or all of the Covered Software due to +statute, judicial order, or regulation then You must: (a) comply with +the terms of this License to the maximum extent possible; and (b) +describe the limitations and the code they affect. Such description must +be placed in a text file included with all distributions of the Covered +Software under this License. Except to the extent prohibited by statute +or regulation, such description must be sufficiently detailed for a +recipient of ordinary skill to be able to understand it. + +5. Termination +-------------- + +5.1. The rights granted under this License will terminate automatically +if You fail to comply with any of its terms. However, if You become +compliant, then the rights granted under this License from a particular +Contributor are reinstated (a) provisionally, unless and until such +Contributor explicitly and finally terminates Your grants, and (b) on an +ongoing basis, if such Contributor fails to notify You of the +non-compliance by some reasonable means prior to 60 days after You have +come back into compliance. Moreover, Your grants from a particular +Contributor are reinstated on an ongoing basis if such Contributor +notifies You of the non-compliance by some reasonable means, this is the +first time You have received notice of non-compliance with this License +from such Contributor, and You become compliant prior to 30 days after +Your receipt of the notice. + +5.2. If You initiate litigation against any entity by asserting a patent +infringement claim (excluding declaratory judgment actions, +counter-claims, and cross-claims) alleging that a Contributor Version +directly or indirectly infringes any patent, then the rights granted to +You by any and all Contributors for the Covered Software under Section +2.1 of this License shall terminate. + +5.3. In the event of termination under Sections 5.1 or 5.2 above, all +end user license agreements (excluding distributors and resellers) which +have been validly granted by You or Your distributors under this License +prior to termination shall survive termination. + +************************************************************************ +* * +* 6. Disclaimer of Warranty * +* ------------------------- * +* * +* Covered Software is provided under this License on an "as is" * +* basis, without warranty of any kind, either expressed, implied, or * +* statutory, including, without limitation, warranties that the * +* Covered Software is free of defects, merchantable, fit for a * +* particular purpose or non-infringing. The entire risk as to the * +* quality and performance of the Covered Software is with You. * +* Should any Covered Software prove defective in any respect, You * +* (not any Contributor) assume the cost of any necessary servicing, * +* repair, or correction. This disclaimer of warranty constitutes an * +* essential part of this License. No use of any Covered Software is * +* authorized under this License except under this disclaimer. * +* * +************************************************************************ + +************************************************************************ +* * +* 7. Limitation of Liability * +* -------------------------- * +* * +* Under no circumstances and under no legal theory, whether tort * +* (including negligence), contract, or otherwise, shall any * +* Contributor, or anyone who distributes Covered Software as * +* permitted above, be liable to You for any direct, indirect, * +* special, incidental, or consequential damages of any character * +* including, without limitation, damages for lost profits, loss of * +* goodwill, work stoppage, computer failure or malfunction, or any * +* and all other commercial damages or losses, even if such party * +* shall have been informed of the possibility of such damages. This * +* limitation of liability shall not apply to liability for death or * +* personal injury resulting from such party's negligence to the * +* extent applicable law prohibits such limitation. Some * +* jurisdictions do not allow the exclusion or limitation of * +* incidental or consequential damages, so this exclusion and * +* limitation may not apply to You. * +* * +************************************************************************ + +8. Litigation +------------- + +Any litigation relating to this License may be brought only in the +courts of a jurisdiction where the defendant maintains its principal +place of business and such litigation shall be governed by laws of that +jurisdiction, without reference to its conflict-of-law provisions. +Nothing in this Section shall prevent a party's ability to bring +cross-claims or counter-claims. + +9. Miscellaneous +---------------- + +This License represents the complete agreement concerning the subject +matter hereof. If any provision of this License is held to be +unenforceable, such provision shall be reformed only to the extent +necessary to make it enforceable. Any law or regulation which provides +that the language of a contract shall be construed against the drafter +shall not be used to construe this License against a Contributor. + +10. Versions of the License +--------------------------- + +10.1. New Versions + +Mozilla Foundation is the license steward. Except as provided in Section +10.3, no one other than the license steward has the right to modify or +publish new versions of this License. Each version will be given a +distinguishing version number. + +10.2. Effect of New Versions + +You may distribute the Covered Software under the terms of the version +of the License under which You originally received the Covered Software, +or under the terms of any subsequent version published by the license +steward. + +10.3. Modified Versions + +If you create software not governed by this License, and you want to +create a new license for such software, you may create and use a +modified version of this License if you rename the license and remove +any references to the name of the license steward (except to note that +such modified license differs from this License). + +10.4. Distributing Source Code Form that is Incompatible With Secondary +Licenses + +If You choose to distribute Source Code Form that is Incompatible With +Secondary Licenses under the terms of this version of the License, the +notice described in Exhibit B of this License must be attached. + +Exhibit A - Source Code Form License Notice +------------------------------------------- + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. + +If it is not possible or desirable to put the notice in a particular +file, then You may include the notice in a location (such as a LICENSE +file in a relevant directory) where a recipient would be likely to look +for such a notice. + +You may add additional accurate notices of copyright ownership. + +Exhibit B - "Incompatible With Secondary Licenses" Notice +--------------------------------------------------------- + + This Source Code Form is "Incompatible With Secondary Licenses", as + defined by the Mozilla Public License, v. 2.0. diff --git a/basics/undertow/README.md b/basics/undertow/README.md new file mode 100644 index 0000000..37bb631 --- /dev/null +++ b/basics/undertow/README.md @@ -0,0 +1,25 @@ +# SeedStack Web sample + +This sample demonstrates a Web application with the Undertow embedded server configured on HTTPS (port 443) and +HTTP/2. + +## Build + +```bash +mvn clean package +``` + +## Run + +```bash +mvn seedstack:run +``` + +Then point your browser to [https://localhost](https://localhost). + +You can also run the tests located in `src/test/java`. + +## Copyright and license + +This source code is copyrighted by [The SeedStack Authors](https://github.com/seedstack/seedstack/blob/master/AUTHORS) and +released under the terms of the [Mozilla Public License 2.0](https://www.mozilla.org/MPL/2.0/). diff --git a/full-apps/catalog-microservice/pom.xml b/basics/undertow/pom.xml similarity index 57% rename from full-apps/catalog-microservice/pom.xml rename to basics/undertow/pom.xml index 4cb897e..a145bfe 100644 --- a/full-apps/catalog-microservice/pom.xml +++ b/basics/undertow/pom.xml @@ -1,4 +1,4 @@ - + - org.seedstack.business - business-migrate -
- - org.hibernate - hibernate-entitymanager - ${hibernate.version} - - - org.hsqldb - hsqldb - ${hsqldb.version} - ch.qos.logback logback-classic @@ -92,12 +60,6 @@ ${assertj.version} test - - org.skyscreamer - jsonassert - ${jsonassert.version} - test - io.rest-assured rest-assured diff --git a/basics/undertow/src/main/java/org/seedstack/samples/undertow/HelloServlet.java b/basics/undertow/src/main/java/org/seedstack/samples/undertow/HelloServlet.java new file mode 100644 index 0000000..4b71638 --- /dev/null +++ b/basics/undertow/src/main/java/org/seedstack/samples/undertow/HelloServlet.java @@ -0,0 +1,22 @@ +/* + * Copyright © 2013-2018, The SeedStack authors + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.seedstack.samples.undertow; + +import java.io.IOException; +import javax.servlet.annotation.WebServlet; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +@WebServlet("/") +public class HelloServlet extends HttpServlet { + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException { + resp.getWriter().print("Hello World!"); + } +} diff --git a/full-apps/catalog-microservice/src/test/resources/application.override.yaml b/basics/undertow/src/main/resources/application.yaml similarity index 56% rename from full-apps/catalog-microservice/src/test/resources/application.override.yaml rename to basics/undertow/src/main/resources/application.yaml index 7d4ce88..c279848 100644 --- a/full-apps/catalog-microservice/src/test/resources/application.override.yaml +++ b/basics/undertow/src/main/resources/application.yaml @@ -6,10 +6,19 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. # -# Add rest module integration test configuration here +logging: + level: INFO -jpa: - units: - catalogDomain: - properties: - hibernate.hbm2ddl.auto: create +web: + server: + port: 443 + https: true + +crypto: + ssl: + keystore: ssl + keyPassword: changeMe + keystores: + ssl: + path: src/main/resources/master.jks + password: changeMe diff --git a/basics/undertow/src/main/resources/master.jks b/basics/undertow/src/main/resources/master.jks new file mode 100644 index 0000000..f756218 Binary files /dev/null and b/basics/undertow/src/main/resources/master.jks differ diff --git a/basics/undertow/src/test/java/org/seedstack/samples/undertow/UndertowHttpsIT.java b/basics/undertow/src/test/java/org/seedstack/samples/undertow/UndertowHttpsIT.java new file mode 100644 index 0000000..8420d33 --- /dev/null +++ b/basics/undertow/src/test/java/org/seedstack/samples/undertow/UndertowHttpsIT.java @@ -0,0 +1,37 @@ +/* + * Copyright © 2013-2018, The SeedStack authors + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.seedstack.samples.undertow; + +import static org.assertj.core.api.Assertions.assertThat; + +import io.restassured.RestAssured; +import io.restassured.config.SSLConfig; +import io.restassured.response.Response; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.seedstack.seed.Configuration; +import org.seedstack.seed.testing.junit4.SeedITRunner; +import org.seedstack.seed.undertow.LaunchWithUndertow; + +@RunWith(SeedITRunner.class) +@LaunchWithUndertow +public class UndertowHttpsIT { + @Configuration("runtime.web.baseUrl") + private String baseUrl; + + @Test + public void testHelloWorld() throws Exception { + Response response = RestAssured.given() + .config(RestAssured.config().sslConfig(SSLConfig.sslConfig().relaxedHTTPSValidation("SSL"))) + .expect() + .statusCode(200) + .when() + .get(baseUrl); + assertThat(response.body().asString()).isEqualTo("Hello World!"); + } +} diff --git a/basics/websocket/pom.xml b/basics/websocket/pom.xml index c2767df..5093dff 100644 --- a/basics/websocket/pom.xml +++ b/basics/websocket/pom.xml @@ -15,8 +15,8 @@ org.seedstack.samples samples - 18.4 - ../.. + 20.11-SNAPSHOT + ../../pom.xml websocket-sample diff --git a/basics/websocket/src/test/java/org/seedstack/samples/websocket/WebSocketIT.java b/basics/websocket/src/test/java/org/seedstack/samples/websocket/WebSocketIT.java index 48c130a..538bf71 100644 --- a/basics/websocket/src/test/java/org/seedstack/samples/websocket/WebSocketIT.java +++ b/basics/websocket/src/test/java/org/seedstack/samples/websocket/WebSocketIT.java @@ -33,11 +33,11 @@ public class WebSocketIT { private ChatClient chatClient1; @Inject private ChatClient chatClient2; - @Configuration("web.runtime.contextPath") + @Configuration("runtime.web.servlet.contextPath") private String contextPath; - @Configuration("web.runtime.host") + @Configuration("runtime.web.server.host") private String host; - @Configuration("web.runtime.port") + @Configuration("runtime.web.server.port") private int port; private CountDownLatch countDownLatch1 = new CountDownLatch(1); private CountDownLatch countDownLatch2 = new CountDownLatch(1); diff --git a/full-apps/README.md b/full-apps/README.md index 85f5e35..f9e542e 100644 --- a/full-apps/README.md +++ b/full-apps/README.md @@ -1,4 +1,4 @@ -# SeedStack add-on samples +# SeedStack full applications samples Each sample in this directory demonstrates multiple features wrapped in a full application. @@ -6,6 +6,6 @@ Each sample in this directory demonstrates multiple features wrapped in a full a | Sample | Directory | Demonstrated feature(s) | |---|---|---| -| Catalog microservice| [catalog-microservice](https://github.com/seedstack/samples/tree/master/addons/catalog-microservice) | A product catalog REST micro-service | -| DDD sample| [ddd](https://github.com/seedstack/samples/tree/master/addons/ddd) | Sophisticated Domain-Driven Design model using the business framework | -| Store webapp | [store-webapp](https://github.com/seedstack/samples/tree/master/addons/store-webapp) | Classic Web application with an self-served W20 frontend | +| Catalog microservice| [catalog-microservice](https://github.com/seedstack/samples/tree/master/full-apps/catalog-microservice) | A product catalog REST micro-service | +| DDD sample| [ddd](https://github.com/seedstack/samples/tree/master/full-apps/ddd) | Sophisticated Domain-Driven Design model using the business framework | +| Store webapp | [store-webapp](https://github.com/seedstack/samples/tree/master/full-apps/store-webapp) | Classic Web application with an self-served W20 frontend | diff --git a/full-apps/catalog-microservice b/full-apps/catalog-microservice new file mode 160000 index 0000000..a632f81 --- /dev/null +++ b/full-apps/catalog-microservice @@ -0,0 +1 @@ +Subproject commit a632f81d70e4a9677e9c70b8a40dd72a4c09afe8 diff --git a/full-apps/catalog-microservice/README.md b/full-apps/catalog-microservice/README.md deleted file mode 100644 index dc84027..0000000 --- a/full-apps/catalog-microservice/README.md +++ /dev/null @@ -1,20 +0,0 @@ -# SeedStack hypermedia micro-service sample - -A micro-service project demonstrating REST features of SeedStack: JAX-RS integration, hypermedia and JSON-home. - -## Run - - mvn seedstack:run - -## Usage - -Discover all application entry points as a JSON-HOME resource on the following URL: - - http://localhost:8080/ - -Then follow the links to HAL resources. - -## Copyright and license - -This source code is copyrighted by [The SeedStack Authors](https://github.com/seedstack/seedstack/blob/master/AUTHORS) and -released under the terms of the [Mozilla Public License 2.0](https://www.mozilla.org/MPL/2.0/). diff --git a/full-apps/catalog-microservice/src/main/java/org/seedstack/samples/catalog/application/Config.java b/full-apps/catalog-microservice/src/main/java/org/seedstack/samples/catalog/application/Config.java deleted file mode 100644 index 5d6b47c..0000000 --- a/full-apps/catalog-microservice/src/main/java/org/seedstack/samples/catalog/application/Config.java +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright © 2013-2018, The SeedStack authors - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ - -package org.seedstack.samples.catalog.application; - -/** - * Regroups some constants related to the application configuration. - */ -public class Config { - /** - * The name of application's JPA unit. - */ - public static final String JPA_UNIT = "catalogDomain"; -} diff --git a/full-apps/catalog-microservice/src/main/java/org/seedstack/samples/catalog/domain/model/product/Price.java b/full-apps/catalog-microservice/src/main/java/org/seedstack/samples/catalog/domain/model/product/Price.java deleted file mode 100644 index d244956..0000000 --- a/full-apps/catalog-microservice/src/main/java/org/seedstack/samples/catalog/domain/model/product/Price.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright © 2013-2018, The SeedStack authors - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ - -package org.seedstack.samples.catalog.domain.model.product; - -import javax.persistence.Embeddable; -import org.seedstack.business.domain.BaseValueObject; - -/** - * A price value object. - */ -@Embeddable -public class Price extends BaseValueObject { - - private float amount; - - private String currency; - - Price() { - } - - /** - * Constructor. - * - * @param amount the amount - * @param currency the currency - */ - public Price(float amount, String currency) { - if (amount < 0) { - throw new IllegalArgumentException("The price amount can't be negative: " + amount); - } - this.amount = amount; - this.currency = currency; - } - - /** - * @return the amount - */ - public float getAmount() { - return amount; - } - - /** - * @return the currency - */ - public String getCurrency() { - return currency; - } - - @Override - public String toString() { - return amount + " " + currency; - } -} diff --git a/full-apps/catalog-microservice/src/main/java/org/seedstack/samples/catalog/domain/model/product/Product.java b/full-apps/catalog-microservice/src/main/java/org/seedstack/samples/catalog/domain/model/product/Product.java deleted file mode 100644 index 40bddaf..0000000 --- a/full-apps/catalog-microservice/src/main/java/org/seedstack/samples/catalog/domain/model/product/Product.java +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright © 2013-2018, The SeedStack authors - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ - -package org.seedstack.samples.catalog.domain.model.product; - -import java.net.URI; -import java.util.HashSet; -import java.util.Set; -import javax.persistence.ElementCollection; -import javax.persistence.Entity; -import javax.persistence.Id; -import org.hibernate.annotations.Type; -import org.seedstack.business.data.DataSet; -import org.seedstack.business.domain.BaseAggregateRoot; - -/** - * The product entity. - */ -@Entity -@DataSet(group = "catalog", name = "product") -public class Product extends BaseAggregateRoot { - - @Id - private String name; - - private URI picture; - - private Price pricing; - - @Type(type = "text") - private String description; - - @ElementCollection - private Set tags = new HashSet(); - - @ElementCollection - private Set details = new HashSet(); - - @ElementCollection - private Set related = new HashSet(); - - Product() { - } - - /** - * Constructor. - * - * @param name the product name - */ - public Product(String name) { - this.name = name; - } - - public String getName() { - return name; - } - - public URI getPicture() { - return picture; - } - - public void setPicture(URI picture) { - this.picture = picture; - } - - public Price getPricing() { - return pricing; - } - - public void setPricing(Price pricing) { - this.pricing = pricing; - } - - public String getDescription() { - return description; - } - - public void setDescription(String description) { - this.description = description; - } - - public Set getTags() { - return tags; - } - - public void tag(String tag) { - this.tags.add(tag); - } - - public Set getDetails() { - return details; - } - - public void addDetails(String details) { - this.details.add(details); - } - - public Set getRelated() { - return related; - } - - public void addRelated(String related) { - this.related.add(related); - } - - @Override - public String getId() { - return this.name; - } -} diff --git a/full-apps/catalog-microservice/src/main/java/org/seedstack/samples/catalog/domain/model/product/Tag.java b/full-apps/catalog-microservice/src/main/java/org/seedstack/samples/catalog/domain/model/product/Tag.java deleted file mode 100644 index 29a069b..0000000 --- a/full-apps/catalog-microservice/src/main/java/org/seedstack/samples/catalog/domain/model/product/Tag.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright © 2013-2018, The SeedStack authors - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ - -package org.seedstack.samples.catalog.domain.model.product; - -import org.seedstack.business.domain.BaseValueObject; - -/** - * A tag value object. - */ - -public class Tag extends BaseValueObject { - private String name; - - /** - * Constructor. - * - * @param name the tag value - */ - public Tag(String name) { - this.name = name; - } - - /** - * @return the tag name - */ - public String getName() { - return name; - } - - @Override - public String toString() { - return name; - } - -} diff --git a/full-apps/catalog-microservice/src/main/java/org/seedstack/samples/catalog/infrastructure/jpa/ProductsJpaFinder.java b/full-apps/catalog-microservice/src/main/java/org/seedstack/samples/catalog/infrastructure/jpa/ProductsJpaFinder.java deleted file mode 100644 index 5bb8eab..0000000 --- a/full-apps/catalog-microservice/src/main/java/org/seedstack/samples/catalog/infrastructure/jpa/ProductsJpaFinder.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright © 2013-2018, The SeedStack authors - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ - -package org.seedstack.samples.catalog.infrastructure.jpa; - -import com.google.inject.Inject; -import java.util.List; -import javax.persistence.EntityManager; -import javax.persistence.TypedQuery; -import javax.persistence.criteria.CriteriaBuilder; -import javax.persistence.criteria.CriteriaQuery; -import javax.persistence.criteria.Root; -import org.seedstack.business.assembler.FluentAssembler; -import org.seedstack.business.finder.BaseRangeFinder; -import org.seedstack.business.finder.Range; -import org.seedstack.business.finder.Result; -import org.seedstack.business.view.Page; -import org.seedstack.business.view.PaginatedView; -import org.seedstack.jpa.JpaUnit; -import org.seedstack.samples.catalog.application.Config; -import org.seedstack.samples.catalog.domain.model.product.Product; -import org.seedstack.samples.catalog.interfaces.rest.catalog.ProductsFinder; -import org.seedstack.samples.catalog.interfaces.rest.product.ProductRepresentation; -import org.seedstack.seed.transaction.Transactional; - -@Transactional -@JpaUnit(Config.JPA_UNIT) -class ProductsJpaFinder extends BaseRangeFinder implements ProductsFinder { - @Inject - private EntityManager entityManager; - - @Inject - private FluentAssembler fluently; - - @Override - public PaginatedView findProducts(Page page, String query) { - Result result = find(Range.rangeFromPageInfo(page.getIndex(), page.getCapacity()), - query); - return new PaginatedView<>(result, page); - } - - private String buildSqlLikeQuery(String query) { - return "%" + query + "%"; - } - - @Override - protected List computeResultList(Range range, String criteria) { - CriteriaBuilder cb = entityManager.getCriteriaBuilder(); - CriteriaQuery q = cb.createQuery(Product.class); - Root product = q.from(Product.class); - - q.select(product); - if (criteria != null) { - q.where(cb.like(product.get("name"), buildSqlLikeQuery(criteria))); - } - - TypedQuery query = entityManager.createQuery(q); - if (range != null) { - query.setFirstResult((int) range.getOffset()).setMaxResults((int) range.getSize()); - } - - return fluently.assemble(query.getResultList()).toListOf(ProductRepresentation.class); - } - - @Override - protected long computeFullRequestSize(String criteria) { - CriteriaBuilder cb = entityManager.getCriteriaBuilder(); - CriteriaQuery q = cb.createQuery(Long.class); - Root product = q.from(Product.class); - q.select(cb.count(product)); - if (criteria != null) { - q.where(cb.like(product.get("name"), buildSqlLikeQuery(criteria))); - } - return entityManager.createQuery(q).getSingleResult(); - } -} diff --git a/full-apps/catalog-microservice/src/main/java/org/seedstack/samples/catalog/infrastructure/jpa/TagJpaFinder.java b/full-apps/catalog-microservice/src/main/java/org/seedstack/samples/catalog/infrastructure/jpa/TagJpaFinder.java deleted file mode 100644 index 84196ea..0000000 --- a/full-apps/catalog-microservice/src/main/java/org/seedstack/samples/catalog/infrastructure/jpa/TagJpaFinder.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright © 2013-2018, The SeedStack authors - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ - -package org.seedstack.samples.catalog.infrastructure.jpa; - -import com.google.inject.Inject; -import java.util.List; -import javax.inject.Named; -import javax.persistence.EntityManager; -import javax.persistence.criteria.CriteriaBuilder; -import javax.persistence.criteria.CriteriaQuery; -import javax.persistence.criteria.Root; -import org.seedstack.business.assembler.FluentAssembler; -import org.seedstack.business.finder.BaseRangeFinder; -import org.seedstack.business.finder.Range; -import org.seedstack.business.finder.Result; -import org.seedstack.business.view.Page; -import org.seedstack.business.view.PaginatedView; -import org.seedstack.jpa.JpaUnit; -import org.seedstack.samples.catalog.application.Config; -import org.seedstack.samples.catalog.domain.model.product.Product; -import org.seedstack.samples.catalog.interfaces.rest.product.ProductRepresentation; -import org.seedstack.samples.catalog.interfaces.rest.tags.TagFinder; -import org.seedstack.seed.transaction.Transactional; - -@Named("tag") -@Transactional -@JpaUnit(Config.JPA_UNIT) -class TagJpaFinder extends BaseRangeFinder implements TagFinder { - @Inject - private EntityManager entityManager; - - @Inject - private FluentAssembler fluently; - - @Override - public PaginatedView findProductsByTag(Page page, String tagName) { - Result result = find(Range.rangeFromPageInfo(page.getIndex(), page.getCapacity()), - tagName); - return new PaginatedView<>(result, page); - } - - @Override - protected List computeResultList(Range range, String tagName) { - CriteriaBuilder cb = entityManager.getCriteriaBuilder(); - CriteriaQuery q = cb.createQuery(Product.class); - Root root = q.from(Product.class); - q.select(root) - .where(cb.isMember(tagName, root.>get("tags"))); - - List products; - if (range != null) { - products = entityManager.createQuery(q) - .setFirstResult((int) range.getOffset()) - .setMaxResults((int) range.getSize()) - .getResultList(); - } else { - products = entityManager.createQuery(q).getResultList(); - } - - return fluently.assemble(products).toListOf(ProductRepresentation.class); - } - - @Override - protected long computeFullRequestSize(String tagName) { - CriteriaBuilder cb = entityManager.getCriteriaBuilder(); - CriteriaQuery q = cb.createQuery(Long.class); - Root root = q.from(Product.class); - q.select(cb.count(root)) - .where(cb.isMember(tagName, root.>get("tags"))); - - return entityManager.createQuery(q).getSingleResult(); - } -} diff --git a/full-apps/catalog-microservice/src/main/java/org/seedstack/samples/catalog/interfaces/data/ProductImporter.java b/full-apps/catalog-microservice/src/main/java/org/seedstack/samples/catalog/interfaces/data/ProductImporter.java deleted file mode 100644 index 209c5fc..0000000 --- a/full-apps/catalog-microservice/src/main/java/org/seedstack/samples/catalog/interfaces/data/ProductImporter.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright © 2013-2018, The SeedStack authors - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ - -package org.seedstack.samples.catalog.interfaces.data; - -import java.security.SecureRandom; -import java.util.ArrayList; -import java.util.List; -import java.util.stream.Stream; -import javax.inject.Inject; -import org.seedstack.business.data.BaseDataImporter; -import org.seedstack.business.domain.Repository; -import org.seedstack.jpa.Jpa; -import org.seedstack.jpa.JpaUnit; -import org.seedstack.samples.catalog.application.Config; -import org.seedstack.samples.catalog.domain.model.product.Product; -import org.seedstack.seed.transaction.Transactional; - -class ProductImporter extends BaseDataImporter { - private static final int MAXIMUM_ITERATION = 15; - @Inject - @Jpa - private Repository repository; - private List staging = new ArrayList<>(); - private SecureRandom random = new SecureRandom(); - - @Override - public boolean isInitialized() { - return false; - } - - @Transactional - @JpaUnit(Config.JPA_UNIT) - @Override - public void clear() { - staging.clear(); - repository.clear(); - } - - @Transactional - @JpaUnit(Config.JPA_UNIT) - @Override - public void importData(Stream stream) { - stream.forEach(staging::add); - staging.forEach(this::addRelatedProducts); - staging.forEach(repository::add); - } - - private void addRelatedProducts(Product product) { - int iteration = 0; - // Iterate until it finds 4 unique related products - while (product.getRelated().size() < 4 && iteration < MAXIMUM_ITERATION) { - Product relatedProduct = staging.get(random.nextInt(staging.size())); - product.addRelated(relatedProduct.getId()); - iteration++; - } - } -} diff --git a/full-apps/catalog-microservice/src/main/java/org/seedstack/samples/catalog/interfaces/rest/CatalogRels.java b/full-apps/catalog-microservice/src/main/java/org/seedstack/samples/catalog/interfaces/rest/CatalogRels.java deleted file mode 100644 index e1c5c26..0000000 --- a/full-apps/catalog-microservice/src/main/java/org/seedstack/samples/catalog/interfaces/rest/CatalogRels.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright © 2013-2018, The SeedStack authors - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ - -package org.seedstack.samples.catalog.interfaces.rest; - -/** - * Lists all the REST resource provided by this application. - */ -public class CatalogRels { - - /** - * Catalog resource. Provides a list of products. - */ - public static final String CATALOG = "catalog"; - - /** - * Product resource. Exposes all the information about a specific product. - */ - public static final String PRODUCT = "product"; - - /** - * Product's tags sub resource. Exposes all the tags of a product. - */ - public static final String PRODUCT_TAGS = "tags"; - - /** - * Tag resource. Exposes all the products corresponding to a tag. - */ - public static final String TAG = "tag"; - - /** - * All products related to another products. - */ - public static final String PRODUCT_RELATED = "related"; -} diff --git a/full-apps/catalog-microservice/src/main/java/org/seedstack/samples/catalog/interfaces/rest/PageInfo.java b/full-apps/catalog-microservice/src/main/java/org/seedstack/samples/catalog/interfaces/rest/PageInfo.java deleted file mode 100644 index 699f38c..0000000 --- a/full-apps/catalog-microservice/src/main/java/org/seedstack/samples/catalog/interfaces/rest/PageInfo.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright © 2013-2018, The SeedStack authors - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ - -package org.seedstack.samples.catalog.interfaces.rest; - -import javax.validation.constraints.Min; -import javax.ws.rs.DefaultValue; -import javax.ws.rs.QueryParam; -import org.seedstack.business.view.Page; - -public class PageInfo { - - public static final String PAGE_INDEX = "pageIndex"; - public static final String PAGE_SIZE = "pageSize"; - - @Min(0) - @DefaultValue("0") - @QueryParam(PAGE_INDEX) - public int pageIndex; - - @Min(1) - @DefaultValue("10") - @QueryParam(PAGE_SIZE) - public int pageSize; - - public Page page() { - return new Page(pageIndex, pageSize); - } - - public int getPageIndex() { - return pageIndex; - } - - public void setPageIndex(int pageIndex) { - this.pageIndex = pageIndex; - } - - public int getPageSize() { - return pageSize; - } - - public void setPageSize(int pageSize) { - this.pageSize = pageSize; - } -} diff --git a/full-apps/catalog-microservice/src/main/java/org/seedstack/samples/catalog/interfaces/rest/catalog/ProductsFinder.java b/full-apps/catalog-microservice/src/main/java/org/seedstack/samples/catalog/interfaces/rest/catalog/ProductsFinder.java deleted file mode 100644 index 3445fb4..0000000 --- a/full-apps/catalog-microservice/src/main/java/org/seedstack/samples/catalog/interfaces/rest/catalog/ProductsFinder.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright © 2013-2018, The SeedStack authors - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ - -package org.seedstack.samples.catalog.interfaces.rest.catalog; - -import org.seedstack.business.finder.Finder; -import org.seedstack.business.finder.RangeFinder; -import org.seedstack.business.view.Page; -import org.seedstack.business.view.PaginatedView; -import org.seedstack.samples.catalog.interfaces.rest.product.ProductRepresentation; - -/** - * This finder provides paginated queries on {@link org.seedstack.samples.catalog.domain.model.product.Product}. - */ -@Finder -public interface ProductsFinder extends RangeFinder { - - /** - * Finds a list of products. The list will be paginated according to the given page. - * An optional query can also be passed. It will filter the products based on their name. - * - * @param page the expected page - * @param query the filter query - * @return the paginated view of product representations - */ - PaginatedView findProducts(Page page, String query); -} diff --git a/full-apps/catalog-microservice/src/main/java/org/seedstack/samples/catalog/interfaces/rest/catalog/ProductsRepresentation.java b/full-apps/catalog-microservice/src/main/java/org/seedstack/samples/catalog/interfaces/rest/catalog/ProductsRepresentation.java deleted file mode 100644 index c830385..0000000 --- a/full-apps/catalog-microservice/src/main/java/org/seedstack/samples/catalog/interfaces/rest/catalog/ProductsRepresentation.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright © 2013-2018, The SeedStack authors - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ - -package org.seedstack.samples.catalog.interfaces.rest.catalog; - -import org.seedstack.business.view.PaginatedView; -import org.seedstack.samples.catalog.interfaces.rest.product.ProductRepresentation; -import org.seedstack.seed.rest.hal.HalRepresentation; - -public class ProductsRepresentation extends HalRepresentation { - - private long totalProduct; - - private long currentPage; - - // required by jackson - ProductsRepresentation() { - } - - public ProductsRepresentation(PaginatedView page) { - this.totalProduct = page.getResultSize(); - this.currentPage = page.getPageIndex(); - if (page.getView().size() > 0) { - embedded("products", page.getView()); - } - } - - public long getTotalProduct() { - return totalProduct; - } - - public long getCurrentPage() { - return currentPage; - } -} diff --git a/full-apps/catalog-microservice/src/main/java/org/seedstack/samples/catalog/interfaces/rest/catalog/ProductsResource.java b/full-apps/catalog-microservice/src/main/java/org/seedstack/samples/catalog/interfaces/rest/catalog/ProductsResource.java deleted file mode 100644 index e299c7f..0000000 --- a/full-apps/catalog-microservice/src/main/java/org/seedstack/samples/catalog/interfaces/rest/catalog/ProductsResource.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright © 2013-2018, The SeedStack authors - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ - -package org.seedstack.samples.catalog.interfaces.rest.catalog; - -import static org.seedstack.samples.catalog.interfaces.rest.PageInfo.PAGE_INDEX; -import static org.seedstack.samples.catalog.interfaces.rest.PageInfo.PAGE_SIZE; - -import io.swagger.annotations.Api; -import javax.inject.Inject; -import javax.validation.Valid; -import javax.ws.rs.BeanParam; -import javax.ws.rs.GET; -import javax.ws.rs.Path; -import javax.ws.rs.Produces; -import javax.ws.rs.QueryParam; -import javax.ws.rs.core.MediaType; -import org.seedstack.business.view.Page; -import org.seedstack.business.view.PaginatedView; -import org.seedstack.samples.catalog.interfaces.rest.CatalogRels; -import org.seedstack.samples.catalog.interfaces.rest.PageInfo; -import org.seedstack.samples.catalog.interfaces.rest.product.ProductRepresentation; -import org.seedstack.seed.rest.Rel; -import org.seedstack.seed.rest.RelRegistry; -import org.seedstack.seed.rest.hal.HalRepresentation; - -@Api -@Path("/products") -public class ProductsResource { - - @Inject - private ProductsFinder productsFinder; - - @Inject - private RelRegistry relRegistry; - - @GET - @Rel(value = CatalogRels.CATALOG, home = true) - @Produces({MediaType.APPLICATION_JSON, "application/hal+json"}) - public HalRepresentation products(@QueryParam("q") String filter, @Valid @BeanParam PageInfo pageInfo) { - PaginatedView view = productsFinder.findProducts(pageInfo.page(), filter); - return buildHalRepresentation(pageInfo, view); - } - - private HalRepresentation buildHalRepresentation(@BeanParam PageInfo pageInfo, - PaginatedView view) { - HalRepresentation representation = new ProductsRepresentation(view) - .self(relRegistry.uri(CatalogRels.CATALOG) - .set(PAGE_INDEX, pageInfo.pageIndex) - .set(PAGE_SIZE, pageInfo.pageSize).getHref()); - - if (view.hasNext()) { - addPageLink(representation, "next", view.next()); - } - if (view.hasPrev()) { - addPageLink(representation, "prev", view.prev()); - } - return representation; - } - - private void addPageLink(HalRepresentation representation, String link, Page page) { - representation.link(link, relRegistry.uri(CatalogRels.CATALOG) - .set(PAGE_INDEX, page.getIndex()).set(PAGE_SIZE, page.getCapacity()).getHref()); - } -} diff --git a/full-apps/catalog-microservice/src/main/java/org/seedstack/samples/catalog/interfaces/rest/catalog/TagRepresentation.java b/full-apps/catalog-microservice/src/main/java/org/seedstack/samples/catalog/interfaces/rest/catalog/TagRepresentation.java deleted file mode 100644 index cae1559..0000000 --- a/full-apps/catalog-microservice/src/main/java/org/seedstack/samples/catalog/interfaces/rest/catalog/TagRepresentation.java +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright © 2013-2018, The SeedStack authors - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ - -package org.seedstack.samples.catalog.interfaces.rest.catalog; - -public class TagRepresentation { - private String name; - - public TagRepresentation(String name) { - this.name = name; - } - - public String getName() { - return name; - } -} diff --git a/full-apps/catalog-microservice/src/main/java/org/seedstack/samples/catalog/interfaces/rest/product/ProductAssembler.java b/full-apps/catalog-microservice/src/main/java/org/seedstack/samples/catalog/interfaces/rest/product/ProductAssembler.java deleted file mode 100644 index 17f4888..0000000 --- a/full-apps/catalog-microservice/src/main/java/org/seedstack/samples/catalog/interfaces/rest/product/ProductAssembler.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright © 2013-2018, The SeedStack authors - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ - -package org.seedstack.samples.catalog.interfaces.rest.product; - -import javax.inject.Inject; -import org.modelmapper.ModelMapper; -import org.seedstack.business.modelmapper.ModelMapperAssembler; -import org.seedstack.samples.catalog.domain.model.product.Product; -import org.seedstack.samples.catalog.interfaces.rest.CatalogRels; -import org.seedstack.seed.rest.RelRegistry; - -public class ProductAssembler extends ModelMapperAssembler { - @Inject - private RelRegistry relRegistry; - - @Override - protected void configure(ModelMapper modelMapper) { - modelMapper.createTypeMap(Product.class, ProductRepresentation.class).addMappings(mapper -> { - mapper.using(ctx -> relRegistry.uri(CatalogRels.PRODUCT).set("title", ctx.getSource()).getHref()) - .map(Product::getName, ProductRepresentation::setSelf); - mapper.using(ctx -> relRegistry.uri(CatalogRels.PRODUCT_TAGS).set("title", ctx.getSource()).getHref()) - .map(Product::getName, ProductRepresentation::setTags); - mapper.using(ctx -> relRegistry.uri(CatalogRels.PRODUCT_RELATED).set("title", ctx.getSource()).getHref()) - .map(Product::getName, ProductRepresentation::setRelated); - }); - } -} diff --git a/full-apps/catalog-microservice/src/main/java/org/seedstack/samples/catalog/interfaces/rest/product/ProductRepresentation.java b/full-apps/catalog-microservice/src/main/java/org/seedstack/samples/catalog/interfaces/rest/product/ProductRepresentation.java deleted file mode 100644 index 9376fe5..0000000 --- a/full-apps/catalog-microservice/src/main/java/org/seedstack/samples/catalog/interfaces/rest/product/ProductRepresentation.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright © 2013-2018, The SeedStack authors - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ - -package org.seedstack.samples.catalog.interfaces.rest.product; - -import java.net.URI; -import java.util.List; -import org.seedstack.business.assembler.DtoOf; -import org.seedstack.samples.catalog.domain.model.product.Product; -import org.seedstack.samples.catalog.interfaces.rest.CatalogRels; -import org.seedstack.seed.rest.hal.HalRepresentation; - -@DtoOf(Product.class) -public class ProductRepresentation extends HalRepresentation { - - private String name; - - private URI picture; - - private String pricing; - - private String description; - - private List details; - - public void setTags(String uri) { - link(CatalogRels.PRODUCT_TAGS, uri); - } - - public void setRelated(String uri) { - link(CatalogRels.PRODUCT_RELATED, uri); - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public void setSelf(String uri) { - self(uri); - } - - public URI getPicture() { - return picture; - } - - public void setPicture(URI picture) { - this.picture = picture; - } - - public String getPricing() { - return pricing; - } - - public void setPricing(String pricing) { - this.pricing = pricing; - } - - public String getDescription() { - return description; - } - - public void setDescription(String description) { - this.description = description; - } - - public List getDetails() { - return details; - } - - public void setDetails(List details) { - this.details = details; - } -} diff --git a/full-apps/catalog-microservice/src/main/java/org/seedstack/samples/catalog/interfaces/rest/product/ProductResource.java b/full-apps/catalog-microservice/src/main/java/org/seedstack/samples/catalog/interfaces/rest/product/ProductResource.java deleted file mode 100644 index d4a1c95..0000000 --- a/full-apps/catalog-microservice/src/main/java/org/seedstack/samples/catalog/interfaces/rest/product/ProductResource.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright © 2013-2018, The SeedStack authors - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ - -package org.seedstack.samples.catalog.interfaces.rest.product; - -import com.google.inject.Inject; -import io.swagger.annotations.Api; -import java.util.List; -import java.util.Objects; -import java.util.stream.Collectors; -import javax.ws.rs.GET; -import javax.ws.rs.NotFoundException; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.core.MediaType; -import org.seedstack.business.assembler.FluentAssembler; -import org.seedstack.business.domain.LegacyRepository; -import org.seedstack.jpa.Jpa; -import org.seedstack.jpa.JpaUnit; -import org.seedstack.samples.catalog.application.Config; -import org.seedstack.samples.catalog.domain.model.product.Product; -import org.seedstack.samples.catalog.interfaces.rest.CatalogRels; -import org.seedstack.samples.catalog.interfaces.rest.catalog.TagRepresentation; -import org.seedstack.seed.rest.Rel; -import org.seedstack.seed.rest.RelRegistry; -import org.seedstack.seed.rest.hal.HalBuilder; -import org.seedstack.seed.rest.hal.HalRepresentation; -import org.seedstack.seed.transaction.Transactional; - -@Api -@Transactional -@JpaUnit(Config.JPA_UNIT) -@Path("/products/{title}") -@Produces({MediaType.APPLICATION_JSON, "application/hal+json"}) -public class ProductResource { - - private static final String PRODUCT_DOES_NOT_EXIST = "Product %s doesn't exist"; - - @Inject - @Jpa - private LegacyRepository repository; - @Inject - private RelRegistry relRegistry; - @Inject - private FluentAssembler fluently; - - @PathParam("title") - private String productName; - - @GET - @Rel(value = CatalogRels.PRODUCT, home = true) - public ProductRepresentation getProduct() { - Product product = repository.load(productName); - if (product == null) { - throw new NotFoundException(String.format(PRODUCT_DOES_NOT_EXIST, productName)); - } - return fluently.assemble(product).to(ProductRepresentation.class); - } - - @GET - @Path("/tags") - @Rel(CatalogRels.PRODUCT_TAGS) - public HalRepresentation getTags() { - Product product = repository.load(productName); - if (product == null) { - throw new NotFoundException(String.format(PRODUCT_DOES_NOT_EXIST, productName)); - } - String selfLink = relRegistry.uri(CatalogRels.PRODUCT_TAGS).set("title", productName).getHref(); - List tagRepresentations = product.getTags().stream() - .map(this::buildHalTag).collect(Collectors.toList()); - return HalBuilder.create(null).self(selfLink).embedded("tags", tagRepresentations); - } - - private HalRepresentation buildHalTag(String tagName) { - return HalBuilder.create(new TagRepresentation(tagName)) - .self(relRegistry.uri(CatalogRels.TAG).set("tagName", tagName).getHref()); - } - - @GET - @Path("/related") - @Rel(CatalogRels.PRODUCT_RELATED) - public HalRepresentation getRelated() { - Product product = repository.load(productName); - if (product == null) { - throw new NotFoundException(String.format(PRODUCT_DOES_NOT_EXIST, productName)); - } - String selfLink = relRegistry.uri(CatalogRels.PRODUCT_RELATED).set("title", productName).getHref(); - List embeddedRelatedProduct = getRelatedProducts(product); - return HalBuilder.create(null).self(selfLink).embedded("related", embeddedRelatedProduct); - } - - private List getRelatedProducts(Product product) { - return product.getRelated().stream() - .map(repository::load).filter(Objects::nonNull) - .map(relatedProduct -> fluently.assemble(relatedProduct).to(ProductRepresentation.class)) - .collect(Collectors.toList()); - } -} diff --git a/full-apps/catalog-microservice/src/main/java/org/seedstack/samples/catalog/interfaces/rest/tags/TagFinder.java b/full-apps/catalog-microservice/src/main/java/org/seedstack/samples/catalog/interfaces/rest/tags/TagFinder.java deleted file mode 100644 index bd39bab..0000000 --- a/full-apps/catalog-microservice/src/main/java/org/seedstack/samples/catalog/interfaces/rest/tags/TagFinder.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright © 2013-2018, The SeedStack authors - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ - -package org.seedstack.samples.catalog.interfaces.rest.tags; - -import org.seedstack.business.finder.Finder; -import org.seedstack.business.finder.RangeFinder; -import org.seedstack.business.view.Page; -import org.seedstack.business.view.PaginatedView; -import org.seedstack.samples.catalog.interfaces.rest.product.ProductRepresentation; - -/** - * Provides method to find tags. - */ -@Finder -public interface TagFinder extends RangeFinder { - - /** - * Finds the products associated to a given tag. - * - * @param page the required page - * @param tagName the tag name - * @return a product page - */ - PaginatedView findProductsByTag(Page page, String tagName); -} diff --git a/full-apps/catalog-microservice/src/main/java/org/seedstack/samples/catalog/interfaces/rest/tags/TagRepresentation.java b/full-apps/catalog-microservice/src/main/java/org/seedstack/samples/catalog/interfaces/rest/tags/TagRepresentation.java deleted file mode 100644 index 28a641b..0000000 --- a/full-apps/catalog-microservice/src/main/java/org/seedstack/samples/catalog/interfaces/rest/tags/TagRepresentation.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright © 2013-2018, The SeedStack authors - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ - -package org.seedstack.samples.catalog.interfaces.rest.tags; - -import org.seedstack.business.view.PaginatedView; -import org.seedstack.samples.catalog.interfaces.rest.product.ProductRepresentation; -import org.seedstack.seed.rest.hal.HalRepresentation; - -public class TagRepresentation extends HalRepresentation { - private String name; - private long totalItems; - private long currentPage; - - TagRepresentation() { - // required by jackson - } - - public TagRepresentation(String tagName, PaginatedView view) { - this.name = tagName; - this.totalItems = view.getResultSize(); - this.currentPage = view.getPageIndex(); - if (view.getView().size() > 0) { - embedded("products", view.getView()); - } - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public long getTotalItems() { - return totalItems; - } - - public long getCurrentPage() { - return currentPage; - } -} diff --git a/full-apps/catalog-microservice/src/main/java/org/seedstack/samples/catalog/interfaces/rest/tags/TagResource.java b/full-apps/catalog-microservice/src/main/java/org/seedstack/samples/catalog/interfaces/rest/tags/TagResource.java deleted file mode 100644 index 5d3d267..0000000 --- a/full-apps/catalog-microservice/src/main/java/org/seedstack/samples/catalog/interfaces/rest/tags/TagResource.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright © 2013-2018, The SeedStack authors - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ - -package org.seedstack.samples.catalog.interfaces.rest.tags; - -import static org.seedstack.samples.catalog.interfaces.rest.PageInfo.PAGE_INDEX; -import static org.seedstack.samples.catalog.interfaces.rest.PageInfo.PAGE_SIZE; - -import io.swagger.annotations.Api; -import javax.inject.Inject; -import javax.inject.Named; -import javax.validation.Valid; -import javax.ws.rs.BeanParam; -import javax.ws.rs.GET; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.core.MediaType; -import org.seedstack.business.view.Page; -import org.seedstack.business.view.PaginatedView; -import org.seedstack.samples.catalog.interfaces.rest.CatalogRels; -import org.seedstack.samples.catalog.interfaces.rest.PageInfo; -import org.seedstack.samples.catalog.interfaces.rest.product.ProductRepresentation; -import org.seedstack.seed.rest.Rel; -import org.seedstack.seed.rest.RelRegistry; -import org.seedstack.seed.rest.hal.HalRepresentation; - -@Api -@Rel(CatalogRels.TAG) -@Path("/tags/{tagName}") -public class TagResource { - - @Inject - private RelRegistry relRegistry; - - @Inject - @Named("tag") - private TagFinder tagFinder; - - @GET - @Produces({MediaType.APPLICATION_JSON, "application/hal+json"}) - public HalRepresentation getTag(@PathParam("tagName") String tagName, @Valid @BeanParam PageInfo pageInfo) { - PaginatedView productView = tagFinder.findProductsByTag(pageInfo.page(), tagName); - String selfLink = buildTagURI(tagName, pageInfo.page()); - HalRepresentation tagRepresentation = new TagRepresentation(tagName, productView).self(selfLink); - if (productView.hasPrev()) { - tagRepresentation.link("prev", buildTagURI(tagName, productView.prev())); - } - if (productView.hasNext()) { - tagRepresentation.link("next", buildTagURI(tagName, productView.next())); - } - return tagRepresentation; - } - - private String buildTagURI(String tagName, Page page) { - return relRegistry.uri(CatalogRels.TAG) - .set("tagName", tagName) - .set(PAGE_INDEX, page.getIndex()) - .set(PAGE_SIZE, page.getCapacity()).getHref(); - } -} diff --git a/full-apps/catalog-microservice/src/main/resources/META-INF/data/catalog/json-generator.js b/full-apps/catalog-microservice/src/main/resources/META-INF/data/catalog/json-generator.js deleted file mode 100644 index 3574a0f..0000000 --- a/full-apps/catalog-microservice/src/main/resources/META-INF/data/catalog/json-generator.js +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright © 2013-2018, The SeedStack authors - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ -/* - * Template used to generate the mocked data on http://www.json-generator.com/ - */ - -[ - { - "group": "catalog", - "name": "product", - "items": [ - '{{repeat(56)}}', - { - "name": '{{company()}}', - "picture": 'https://placeimg.com/700/300/tech', - "pricing": {"amount": '{{floating(0, 500, 2)}}', "currency": "USD"}, - "description": '{{lorem(2, "paragraphs")}}', - "details": [ - '{{repeat(5)}}', - '{{lorem(4, "words")}}' - ], - "tags": [ - '{{repeat(3)}}', - '{{random("tag1", "tag2", "tag3", "tag4", "tag5", "tag6")}}' - ] - } - ] - } -] \ No newline at end of file diff --git a/full-apps/catalog-microservice/src/main/resources/META-INF/data/catalog/product.json b/full-apps/catalog-microservice/src/main/resources/META-INF/data/catalog/product.json deleted file mode 100644 index ff24c60..0000000 --- a/full-apps/catalog-microservice/src/main/resources/META-INF/data/catalog/product.json +++ /dev/null @@ -1,1184 +0,0 @@ -[ - { - "group": "catalog", - "name": "product", - "items": [ - { - "name": "Xixan", - "picture": "https://placeimg.com/700/300/tech", - "pricing": { - "amount": 317.35, - "currency": "USD" - }, - "description": "Ipsum incididunt esse enim dolore eiusmod labore officia reprehenderit sint. Excepteur aute tempor ex eiusmod excepteur quis veniam magna sit. Esse enim adipisicing excepteur cupidatat veniam. Est Lorem mollit eu magna officia adipisicing. Laboris esse dolore enim sint aliqua ad labore non.\r\nEiusmod esse duis labore ea ex veniam consectetur ipsum. Dolore ipsum voluptate cillum mollit labore consectetur ad officia cillum culpa veniam officia nulla labore. Irure quis sint veniam eiusmod labore fugiat in non est officia.\r\n", - "details": [ - "labore in cillum dolor", - "nisi ipsum laboris qui", - "veniam nostrud aute amet", - "nulla ipsum id laborum", - "consectetur laboris adipisicing reprehenderit" - ], - "tags": [ - "tag6", - "tag6", - "tag5" - ] - }, - { - "name": "Skybold", - "picture": "https://placeimg.com/700/300/tech", - "pricing": { - "amount": 321.54, - "currency": "USD" - }, - "description": "Eiusmod esse sunt commodo occaecat ex. Cillum ullamco commodo ut pariatur in. Eiusmod sunt reprehenderit deserunt reprehenderit ea commodo. Ex enim tempor occaecat veniam consequat mollit ullamco reprehenderit cillum duis anim. Proident enim elit ad magna magna consequat adipisicing pariatur ex dolor labore duis do aliquip. Ad ea est ex adipisicing voluptate. Quis eiusmod aute est id sunt nulla mollit anim et aute non eiusmod pariatur.\r\nCulpa excepteur aliqua laboris dolor dolor eu culpa tempor eiusmod incididunt veniam magna. Cillum ea enim amet aliquip sint. Aliqua Lorem pariatur amet culpa proident consectetur anim qui nisi nisi dolor. Nostrud laboris labore minim irure ea et nulla deserunt qui cupidatat magna duis. Tempor exercitation reprehenderit nisi voluptate incididunt reprehenderit occaecat elit nostrud laborum laborum cupidatat et.\r\n", - "details": [ - "ullamco enim minim dolore", - "quis excepteur Lorem non", - "est ullamco eu veniam", - "et ex eiusmod quis", - "veniam magna magna ullamco" - ], - "tags": [ - "tag1", - "tag1", - "tag3" - ] - }, - { - "name": "Gaptec", - "picture": "https://placeimg.com/700/300/tech", - "pricing": { - "amount": 44.05, - "currency": "USD" - }, - "description": "Magna consectetur nostrud consequat est sit deserunt aliquip. Culpa in nulla voluptate occaecat dolore aliquip laboris minim fugiat reprehenderit. Et ea non elit esse in aliquip aliqua eiusmod exercitation. Ut Lorem enim consectetur incididunt quis nisi mollit. Adipisicing esse labore qui do aute irure magna in ad. Aliquip non commodo nulla minim culpa ex sit culpa sunt non velit.\r\nCommodo nostrud veniam duis quis proident mollit id minim. Cillum nisi cillum minim proident eu eiusmod magna nulla duis. Id ipsum est anim eu anim ut.\r\n", - "details": [ - "ullamco labore fugiat adipisicing", - "adipisicing quis eu ut", - "ex ipsum pariatur laborum", - "nisi excepteur fugiat irure", - "excepteur ullamco laborum proident" - ], - "tags": [ - "tag5", - "tag6", - "tag1" - ] - }, - { - "name": "Norali", - "picture": "https://placeimg.com/700/300/tech", - "pricing": { - "amount": 80.66, - "currency": "USD" - }, - "description": "Labore aliquip consectetur quis exercitation aute amet sint sunt nostrud tempor et adipisicing duis nisi. Incididunt nisi nostrud ipsum sit pariatur sint laborum exercitation do ullamco enim. Eu irure mollit cillum ullamco laborum id laborum et amet non nostrud aliqua aliqua aute. Aliqua deserunt excepteur elit eu mollit dolor ex consectetur. Nostrud enim fugiat exercitation ut aute adipisicing sit dolor nostrud irure.\r\nEst minim ad labore reprehenderit eiusmod tempor laboris culpa voluptate esse. Duis cupidatat sit fugiat ex laboris cillum. Ex nulla commodo quis deserunt amet incididunt est non exercitation incididunt reprehenderit et.\r\n", - "details": [ - "incididunt non ut ipsum", - "id dolor id quis", - "anim enim in ea", - "in sunt ipsum irure", - "cillum esse dolore ipsum" - ], - "tags": [ - "tag3", - "tag6", - "tag1" - ] - }, - { - "name": "Bleendot", - "picture": "https://placeimg.com/700/300/tech", - "pricing": { - "amount": 123.82, - "currency": "USD" - }, - "description": "Sit aliqua in cillum deserunt Lorem eu irure aliqua aliquip dolore. Occaecat mollit commodo ex quis. Nulla exercitation commodo cupidatat deserunt. Sit consectetur proident cupidatat ullamco ipsum exercitation id adipisicing anim ea cillum consequat. Aliqua magna excepteur nisi consequat ut eiusmod eiusmod ea deserunt pariatur ad ad. Commodo officia ex exercitation minim veniam ullamco exercitation sunt commodo ex esse. Nulla in pariatur velit ad fugiat.\r\nOccaecat reprehenderit cupidatat occaecat excepteur occaecat velit magna. Aliquip ipsum minim amet dolor quis veniam culpa enim officia ipsum consectetur aliquip mollit. Aliqua id laborum reprehenderit exercitation voluptate anim pariatur aute minim nisi enim Lorem consequat est.\r\n", - "details": [ - "pariatur ipsum qui Lorem", - "qui amet laboris sunt", - "anim eu enim quis", - "duis amet ipsum quis", - "incididunt aute deserunt officia" - ], - "tags": [ - "tag3", - "tag3", - "tag1" - ] - }, - { - "name": "Zytrax", - "picture": "https://placeimg.com/700/300/tech", - "pricing": { - "amount": 377.31, - "currency": "USD" - }, - "description": "Amet voluptate commodo laborum qui aliqua ex eiusmod veniam velit dolore elit nulla commodo. Laboris consectetur sunt qui consectetur reprehenderit adipisicing. Dolor adipisicing nulla mollit ad velit duis consectetur sint exercitation mollit anim irure ex. Tempor nostrud do non commodo consectetur ipsum nulla.\r\nAute quis excepteur sint quis sunt quis dolore dolore eiusmod culpa do nulla ad ex. Do consequat labore sint elit veniam quis quis cillum est duis voluptate. Exercitation non non in reprehenderit cupidatat eu officia ea exercitation laborum ut dolor sit. Esse nisi dolor tempor non nisi reprehenderit. Ullamco in voluptate cupidatat consequat eu veniam aliqua excepteur occaecat labore aute. Ipsum enim fugiat labore velit cupidatat nisi do laborum velit sit.\r\n", - "details": [ - "sit ut occaecat culpa", - "do dolor cillum occaecat", - "commodo incididunt ea et", - "cillum proident veniam velit", - "incididunt et duis ad" - ], - "tags": [ - "tag5", - "tag5", - "tag4" - ] - }, - { - "name": "Exostream", - "picture": "https://placeimg.com/700/300/tech", - "pricing": { - "amount": 84.96, - "currency": "USD" - }, - "description": "Laboris ipsum non ut reprehenderit irure eiusmod sit. Eu cillum excepteur veniam est consectetur commodo id eu magna esse incididunt. Eiusmod officia laborum ipsum incididunt eiusmod labore ullamco laboris. Excepteur incididunt est esse irure aliqua occaecat consequat qui voluptate ullamco adipisicing eu. Fugiat tempor nostrud eiusmod sunt irure aute est magna dolor sit magna Lorem enim occaecat. Exercitation consectetur consectetur labore et magna commodo pariatur do nisi amet duis in do. Incididunt laboris esse fugiat Lorem est commodo elit consequat in proident ullamco.\r\nEiusmod officia id fugiat nulla dolore enim ullamco magna ex incididunt. Anim velit elit Lorem cillum mollit tempor. Pariatur consectetur tempor proident quis esse magna nisi eiusmod pariatur qui adipisicing quis cupidatat amet.\r\n", - "details": [ - "aliquip et magna mollit", - "dolore commodo amet eu", - "excepteur aute id fugiat", - "Lorem adipisicing aliquip esse", - "duis ad minim magna" - ], - "tags": [ - "tag5", - "tag3", - "tag3" - ] - }, - { - "name": "Polarium", - "picture": "https://placeimg.com/700/300/tech", - "pricing": { - "amount": 396.86, - "currency": "USD" - }, - "description": "Ullamco cillum eu cupidatat sunt minim consequat eu deserunt exercitation mollit labore mollit sit. Excepteur culpa cillum tempor labore occaecat sunt fugiat dolore qui ea. Aliqua reprehenderit aliqua id dolor duis eu. Aute dolor sit labore ut quis dolor ullamco consectetur adipisicing do velit. Deserunt velit fugiat tempor eu cupidatat qui et ad voluptate do labore quis eu tempor. Amet quis tempor sint deserunt deserunt irure. Velit esse eiusmod amet ullamco amet ullamco excepteur anim.\r\nMollit quis eiusmod ad magna mollit aliqua. Culpa do consectetur est duis proident nulla quis in proident. Ullamco nisi in duis sint qui sit duis.\r\n", - "details": [ - "deserunt sunt sit culpa", - "nisi velit adipisicing fugiat", - "eiusmod nisi pariatur anim", - "cupidatat ea cillum nostrud", - "fugiat est aute voluptate" - ], - "tags": [ - "tag3", - "tag1", - "tag3" - ] - }, - { - "name": "Comvene", - "picture": "https://placeimg.com/700/300/tech", - "pricing": { - "amount": 270.6, - "currency": "USD" - }, - "description": "Commodo et id et quis laborum fugiat ex laborum reprehenderit irure. Deserunt commodo non pariatur voluptate. Minim id velit occaecat qui ullamco. Amet nisi irure deserunt laboris cillum esse adipisicing excepteur Lorem irure ipsum enim eu duis. Laborum consequat occaecat est occaecat cupidatat amet duis duis aliqua eu occaecat. Laborum commodo occaecat commodo ad reprehenderit. Pariatur aliquip minim est amet.\r\nReprehenderit adipisicing dolor cupidatat aliquip deserunt. Irure fugiat anim labore ex dolore nisi enim excepteur Lorem sint et. Incididunt aliquip do ullamco mollit.\r\n", - "details": [ - "aliqua non laboris tempor", - "proident aliquip nostrud nostrud", - "ipsum deserunt quis laboris", - "eiusmod sunt elit qui", - "ullamco excepteur duis culpa" - ], - "tags": [ - "tag4", - "tag3", - "tag3" - ] - }, - { - "name": "Daido", - "picture": "https://placeimg.com/700/300/tech", - "pricing": { - "amount": 224.69, - "currency": "USD" - }, - "description": "Elit sit pariatur labore amet anim. Fugiat Lorem laborum minim aute cillum officia do non aute ipsum laboris dolor id. Aliqua proident ad elit laborum officia exercitation pariatur reprehenderit nulla est. Proident in duis tempor laboris eu quis et magna Lorem commodo.\r\nId do mollit aute nisi irure in eu cupidatat. Id cupidatat aute eu enim et anim do quis aute ea eu non laborum labore. Do nisi exercitation officia consequat.\r\n", - "details": [ - "cupidatat pariatur pariatur qui", - "labore anim velit do", - "et do exercitation ipsum", - "irure magna ut aliqua", - "voluptate minim est reprehenderit" - ], - "tags": [ - "tag2", - "tag1", - "tag4" - ] - }, - { - "name": "Straloy", - "picture": "https://placeimg.com/700/300/tech", - "pricing": { - "amount": 316.73, - "currency": "USD" - }, - "description": "Sint dolore laboris sit aute eiusmod ullamco magna amet dolore deserunt irure esse aute. Anim qui incididunt ad Lorem sunt. Duis veniam anim do nisi. Sint nisi officia sunt cupidatat exercitation sit et mollit qui consequat exercitation velit laborum proident.\r\nDolor aute consectetur excepteur occaecat minim aliqua cillum sunt ea. Non esse do qui sint culpa id mollit. Ad nostrud adipisicing voluptate eu. Anim cillum excepteur dolore ullamco veniam.\r\n", - "details": [ - "sint exercitation ad aliqua", - "duis cillum dolor et", - "culpa ipsum aliqua nisi", - "consequat duis ut culpa", - "aute anim ex excepteur" - ], - "tags": [ - "tag1", - "tag4", - "tag5" - ] - }, - { - "name": "Opticall", - "picture": "https://placeimg.com/700/300/tech", - "pricing": { - "amount": 489.97, - "currency": "USD" - }, - "description": "Lorem velit non id dolore ut ea labore. Nostrud laborum occaecat nisi aute sint consectetur incididunt id fugiat ipsum occaecat. Anim adipisicing enim commodo mollit magna fugiat esse do esse exercitation duis duis elit. Esse voluptate adipisicing elit incididunt amet eu officia ex aute veniam dolore do. Velit et non pariatur dolore sunt culpa veniam fugiat commodo esse duis.\r\nAute elit exercitation reprehenderit quis minim reprehenderit occaecat occaecat. Aute tempor culpa adipisicing consectetur voluptate duis eiusmod fugiat irure nostrud. Aliqua mollit duis nisi minim labore et ad ea nostrud labore.\r\n", - "details": [ - "Lorem amet magna nostrud", - "pariatur aliquip sint veniam", - "culpa elit nostrud velit", - "qui veniam nisi magna", - "sunt proident anim sunt" - ], - "tags": [ - "tag6", - "tag4", - "tag6" - ] - }, - { - "name": "Xinware", - "picture": "https://placeimg.com/700/300/tech", - "pricing": { - "amount": 364.73, - "currency": "USD" - }, - "description": "Minim reprehenderit aliqua excepteur cupidatat eiusmod veniam ipsum culpa duis quis dolore. Amet tempor adipisicing proident tempor ad nulla est aliqua eiusmod laborum ipsum cupidatat sit. Cupidatat aliqua labore magna ut ea qui sint consequat cillum ipsum qui elit adipisicing.\r\nEst consequat nostrud magna ea tempor sint qui minim. Laboris eu aliqua minim commodo in. Cillum sunt cillum sint consectetur in est non consectetur.\r\n", - "details": [ - "elit amet veniam aliquip", - "minim aliquip elit consequat", - "nulla enim ad eu", - "aliquip adipisicing laborum duis", - "nulla amet aliquip nisi" - ], - "tags": [ - "tag3", - "tag6", - "tag6" - ] - }, - { - "name": "Zanilla", - "picture": "https://placeimg.com/700/300/tech", - "pricing": { - "amount": 193.78, - "currency": "USD" - }, - "description": "Est commodo aute non incididunt. Sint ullamco nisi ea ea deserunt ea quis deserunt incididunt officia proident. Sit reprehenderit Lorem amet cillum aliquip ad ex mollit officia non sint est anim. In dolore non ex do aliqua tempor minim ipsum. Irure occaecat aliquip cillum enim non pariatur sint enim nisi quis. Minim cupidatat elit officia duis est aliqua exercitation adipisicing tempor anim ad tempor.\r\nLaboris dolor consectetur ipsum aliqua. Nostrud amet ad ullamco pariatur consectetur cillum aute nostrud ullamco laboris duis officia proident eu. Irure exercitation exercitation nulla id pariatur duis ipsum aliquip elit fugiat minim pariatur. Ut consequat in voluptate nostrud dolor amet commodo ea ipsum. Dolore labore irure et dolor dolore tempor duis tempor laboris do ut duis.\r\n", - "details": [ - "laboris amet duis anim", - "nulla aliqua id ipsum", - "reprehenderit nisi consectetur labore", - "enim cupidatat proident tempor", - "commodo nulla velit eiusmod" - ], - "tags": [ - "tag1", - "tag1", - "tag3" - ] - }, - { - "name": "Accruex", - "picture": "https://placeimg.com/700/300/tech", - "pricing": { - "amount": 352.35, - "currency": "USD" - }, - "description": "Cillum eu nisi exercitation aliquip non esse amet duis. Voluptate incididunt voluptate minim pariatur excepteur cillum nostrud deserunt deserunt sunt. Commodo proident labore exercitation pariatur reprehenderit dolore sunt velit. Pariatur aliquip Lorem et dolor aliqua dolor duis aute velit. Minim nulla veniam elit cupidatat enim cupidatat ad elit do ad anim qui non aliqua.\r\nAliquip tempor officia elit quis. Amet reprehenderit eu excepteur nisi Lorem culpa nulla duis excepteur. Commodo excepteur ea ullamco est esse do minim velit. Id mollit proident nisi ipsum sit est dolor aute deserunt do nulla anim. Irure laborum est est ipsum et veniam fugiat deserunt veniam reprehenderit adipisicing.\r\n", - "details": [ - "laborum do non magna", - "aliquip magna velit dolor", - "do est quis qui", - "ullamco laboris anim sit", - "ut ad mollit anim" - ], - "tags": [ - "tag4", - "tag6", - "tag1" - ] - }, - { - "name": "Melbacor", - "picture": "https://placeimg.com/700/300/tech", - "pricing": { - "amount": 342.45, - "currency": "USD" - }, - "description": "Officia quis qui pariatur tempor. Sunt reprehenderit ipsum anim dolor culpa veniam est irure laborum pariatur magna occaecat nostrud adipisicing. Non minim qui enim in do aliqua occaecat ullamco velit sunt. Ipsum duis eu elit ut. Dolore nisi sit officia non esse enim ullamco consectetur dolore elit ut aliquip. Elit laborum incididunt consectetur in duis sunt ad incididunt nulla est est.\r\nEiusmod consequat consectetur commodo nisi ex duis ipsum enim nisi non. Fugiat ad veniam deserunt ullamco nostrud ex cillum ipsum. Ex consequat nulla anim in. Aliquip aute laboris tempor quis excepteur cupidatat eiusmod ut consectetur enim et. Quis exercitation magna ipsum duis commodo. Velit est non exercitation sunt amet veniam. Sunt et nostrud cupidatat excepteur cillum magna Lorem dolore velit officia exercitation aute officia labore.\r\n", - "details": [ - "ullamco nostrud nisi eiusmod", - "velit ex enim sit", - "esse est labore et", - "velit nulla sunt adipisicing", - "nulla ullamco dolor aute" - ], - "tags": [ - "tag3", - "tag3", - "tag5" - ] - }, - { - "name": "Assistia", - "picture": "https://placeimg.com/700/300/tech", - "pricing": { - "amount": 192.38, - "currency": "USD" - }, - "description": "Cupidatat irure ipsum sint proident ut fugiat sit aute tempor elit occaecat amet Lorem minim. Irure cupidatat laboris incididunt nulla fugiat id adipisicing ad sunt laborum. Velit dolor culpa eu eiusmod veniam voluptate nulla tempor. Nulla do ut incididunt id. Aliquip ullamco id magna pariatur ut consectetur aute duis Lorem Lorem excepteur nostrud.\r\nCulpa labore aliqua id reprehenderit aliqua dolor veniam esse duis nisi tempor do id exercitation. Tempor pariatur sunt aliquip ipsum ut adipisicing enim incididunt nulla ea dolore. Officia elit labore nostrud nostrud esse.\r\n", - "details": [ - "laboris cupidatat voluptate excepteur", - "sunt nisi reprehenderit minim", - "est deserunt consectetur ipsum", - "ad est aute minim", - "mollit sint amet sit" - ], - "tags": [ - "tag6", - "tag5", - "tag3" - ] - }, - { - "name": "Combot", - "picture": "https://placeimg.com/700/300/tech", - "pricing": { - "amount": 176.12, - "currency": "USD" - }, - "description": "Occaecat qui in sunt culpa duis mollit laboris adipisicing sint exercitation ipsum. Occaecat deserunt pariatur excepteur sint fugiat. Quis aute velit cillum deserunt aliquip dolore aliqua duis incididunt. Voluptate cupidatat aute est cillum commodo amet duis ullamco. Duis ut et officia ipsum aliqua culpa ex sint officia ad irure cupidatat esse dolor. Deserunt excepteur reprehenderit Lorem dolore. Nulla culpa irure ipsum fugiat fugiat reprehenderit aute laborum culpa magna dolor deserunt.\r\nVeniam in do eu consequat ea sint. Proident eiusmod aliquip tempor et veniam laboris nisi pariatur reprehenderit amet proident officia minim. Reprehenderit enim excepteur laborum sit est esse. Incididunt do commodo ullamco qui cillum culpa mollit ea. Dolore laborum proident enim mollit ullamco excepteur commodo occaecat ex eiusmod tempor. In proident velit velit nulla laborum consequat ea voluptate voluptate deserunt.\r\n", - "details": [ - "et aliquip et et", - "non id labore dolore", - "ullamco exercitation ipsum laboris", - "duis anim do eiusmod", - "sint sit aute culpa" - ], - "tags": [ - "tag6", - "tag2", - "tag2" - ] - }, - { - "name": "Gorganic", - "picture": "https://placeimg.com/700/300/tech", - "pricing": { - "amount": 183.46, - "currency": "USD" - }, - "description": "Nisi incididunt anim exercitation excepteur ad eu culpa proident in esse eu. Veniam ipsum laborum dolor et laborum Lorem in mollit magna officia qui dolore irure aliqua. Qui ea dolor voluptate laboris ad. Incididunt tempor aute culpa mollit eu ut commodo laboris officia id. Dolor ad dolore dolor consequat elit occaecat et.\r\nSit dolore commodo et nulla consectetur labore dolor elit veniam dolore veniam elit dolor. Non dolor sint consequat consequat dolore ea laboris. Esse occaecat excepteur fugiat elit minim cupidatat elit ullamco reprehenderit consequat anim magna incididunt. Ut nisi laborum eiusmod tempor nisi nulla laborum quis magna Lorem. Occaecat fugiat laborum sunt sunt qui. Dolor eiusmod consequat elit nulla. Consectetur ad nulla dolor reprehenderit duis mollit.\r\n", - "details": [ - "dolor sint excepteur et", - "officia consectetur amet laboris", - "labore cupidatat adipisicing tempor", - "consequat irure non non", - "do dolore sint exercitation" - ], - "tags": [ - "tag6", - "tag5", - "tag6" - ] - }, - { - "name": "Fangold", - "picture": "https://placeimg.com/700/300/tech", - "pricing": { - "amount": 265.43, - "currency": "USD" - }, - "description": "Proident enim esse occaecat enim. Non proident proident veniam velit nisi proident in minim. Adipisicing qui deserunt sunt laborum sit sit eu proident in enim elit mollit incididunt aliquip. Officia do amet sunt tempor. Eiusmod aliqua eu aliqua enim et proident et ea laboris.\r\nEa officia voluptate enim in qui occaecat deserunt commodo enim ad tempor eiusmod laborum quis. Veniam nisi et laborum incididunt veniam pariatur sint culpa. Anim ut anim fugiat deserunt ipsum magna voluptate fugiat dolore. Magna aliqua ex id sunt nisi do deserunt deserunt consectetur.\r\n", - "details": [ - "velit excepteur dolor est", - "voluptate in culpa incididunt", - "laborum ipsum aliquip duis", - "veniam anim ut consectetur", - "esse incididunt dolore nostrud" - ], - "tags": [ - "tag4", - "tag4", - "tag1" - ] - }, - { - "name": "Entogrok", - "picture": "https://placeimg.com/700/300/tech", - "pricing": { - "amount": 247.42, - "currency": "USD" - }, - "description": "Deserunt consectetur quis excepteur voluptate commodo exercitation est cillum. Sit do ut in aliqua cillum. Officia commodo magna esse et laboris nostrud commodo irure voluptate sunt laboris labore.\r\nConsequat excepteur commodo pariatur sint ullamco ipsum. Magna magna excepteur ex mollit. Dolor elit proident irure incididunt. Occaecat cupidatat duis commodo dolore ex reprehenderit eu ut commodo mollit.\r\n", - "details": [ - "ea fugiat eiusmod elit", - "pariatur consequat eu ullamco", - "proident officia deserunt aliquip", - "sunt nulla incididunt esse", - "do mollit eiusmod dolor" - ], - "tags": [ - "tag3", - "tag3", - "tag1" - ] - }, - { - "name": "Isostream", - "picture": "https://placeimg.com/700/300/tech", - "pricing": { - "amount": 412.45, - "currency": "USD" - }, - "description": "Pariatur sint cupidatat consectetur magna id ut ullamco culpa consectetur. Sit laborum et eu laborum. Adipisicing qui ad in veniam voluptate aliqua ad veniam ut laborum. Dolore laboris ex anim dolor sint sunt ullamco. Voluptate laborum laborum occaecat ipsum anim eiusmod labore anim dolor voluptate magna labore. Elit cupidatat in voluptate et cillum aute fugiat irure. Et magna reprehenderit ullamco ad ut adipisicing fugiat qui excepteur.\r\nIpsum magna excepteur reprehenderit anim cupidatat mollit cupidatat dolor irure officia duis deserunt. Sit sunt non laborum in laborum ad irure. Reprehenderit adipisicing laboris ipsum laboris.\r\n", - "details": [ - "elit deserunt ut aliqua", - "sit velit reprehenderit veniam", - "laboris excepteur est cupidatat", - "fugiat laboris ullamco cillum", - "eu qui quis cupidatat" - ], - "tags": [ - "tag2", - "tag1", - "tag5" - ] - }, - { - "name": "Pushcart", - "picture": "https://placeimg.com/700/300/tech", - "pricing": { - "amount": 265.08, - "currency": "USD" - }, - "description": "Tempor incididunt in sint sit. Proident dolor officia pariatur culpa occaecat occaecat do do ex. Voluptate excepteur ut nisi laborum. Exercitation sunt esse dolore fugiat. Sit consectetur incididunt eu in dolore cillum cillum mollit do minim consequat sint. Nulla esse adipisicing nostrud mollit minim quis cillum minim deserunt in deserunt velit. Tempor commodo id sunt proident eiusmod labore occaecat sunt aliqua amet aliqua velit esse quis.\r\nEu ullamco eiusmod qui irure dolor consequat. Ullamco mollit mollit ex eu qui deserunt irure quis. Adipisicing in aute eiusmod commodo occaecat nostrud duis pariatur officia velit commodo.\r\n", - "details": [ - "ullamco ex ad nostrud", - "est minim eiusmod veniam", - "culpa proident do eu", - "et deserunt aute ea", - "anim non duis nulla" - ], - "tags": [ - "tag3", - "tag6", - "tag5" - ] - }, - { - "name": "Idealis", - "picture": "https://placeimg.com/700/300/tech", - "pricing": { - "amount": 463.55, - "currency": "USD" - }, - "description": "In tempor mollit veniam ut tempor ut dolor cupidatat esse laboris magna elit in. Duis sit magna laboris nulla amet qui adipisicing pariatur voluptate. Fugiat mollit eiusmod esse aliquip et aliqua eiusmod occaecat labore. Amet exercitation amet quis laborum reprehenderit consequat deserunt in. Ad eiusmod non elit nostrud. Occaecat ullamco eiusmod veniam est aute ea sint ullamco ea reprehenderit reprehenderit.\r\nDolor sint laboris anim velit excepteur elit veniam eiusmod. Sunt ut sint consequat qui veniam aliquip elit sunt fugiat officia aliqua. Sunt dolore amet enim anim ut pariatur nisi amet. Cupidatat labore in id aliquip ex dolore aliquip id reprehenderit consectetur occaecat adipisicing est esse. Nulla id deserunt cillum in anim do proident ullamco eu aliquip. Labore magna quis tempor nisi ad ex qui reprehenderit ad.\r\n", - "details": [ - "anim consequat ipsum nisi", - "minim consectetur amet culpa", - "sit exercitation occaecat in", - "ullamco qui irure ipsum", - "commodo officia laboris eiusmod" - ], - "tags": [ - "tag4", - "tag4", - "tag1" - ] - }, - { - "name": "Slofast", - "picture": "https://placeimg.com/700/300/tech", - "pricing": { - "amount": 190.18, - "currency": "USD" - }, - "description": "Adipisicing incididunt fugiat officia excepteur fugiat cillum anim ad cillum mollit sunt. Ea quis cillum dolor quis veniam quis pariatur Lorem. Qui laboris voluptate proident dolore labore voluptate laborum sint aliqua sunt. Irure sint reprehenderit veniam irure cupidatat minim fugiat est cupidatat Lorem. Commodo sunt non irure ex qui culpa pariatur enim exercitation nulla Lorem consequat exercitation irure. Anim incididunt mollit ut esse velit elit aute ex fugiat ea cupidatat. Velit ad officia et pariatur laborum proident.\r\nMollit nisi enim ullamco nostrud laborum fugiat velit velit elit est enim fugiat qui. Excepteur cupidatat qui sit ullamco eu et pariatur excepteur mollit ad nulla. Mollit consequat eu do proident nostrud consectetur dolor. Ut deserunt excepteur laboris quis cupidatat excepteur. Ipsum excepteur consequat reprehenderit occaecat est occaecat enim ex adipisicing elit. Minim Lorem elit anim occaecat ut ex dolor laboris mollit eu est laboris voluptate.\r\n", - "details": [ - "id dolore ea nisi", - "duis Lorem aute sit", - "esse cupidatat in nulla", - "quis ut in proident", - "ullamco nulla Lorem amet" - ], - "tags": [ - "tag2", - "tag1", - "tag4" - ] - }, - { - "name": "Assitia", - "picture": "https://placeimg.com/700/300/tech", - "pricing": { - "amount": 100.73, - "currency": "USD" - }, - "description": "In id elit nostrud do voluptate enim laborum officia ex elit. Est id do elit in et. Sunt ad tempor occaecat ipsum. Nostrud nulla qui mollit Lorem cillum magna magna minim excepteur nostrud ut proident tempor incididunt. Do in dolore veniam duis deserunt eu Lorem.\r\nAute nulla minim irure qui sint velit incididunt. Veniam do duis duis enim laborum velit aliqua reprehenderit dolore. Culpa ipsum dolore sint esse occaecat et tempor aliquip irure reprehenderit. Labore reprehenderit pariatur ex eiusmod dolore culpa cupidatat Lorem aute culpa. Proident non cupidatat laboris ut magna reprehenderit veniam id.\r\n", - "details": [ - "Lorem quis elit nulla", - "nulla duis aliqua do", - "proident in amet do", - "enim exercitation ex do", - "enim sunt minim anim" - ], - "tags": [ - "tag1", - "tag4", - "tag5" - ] - }, - { - "name": "Koogle", - "picture": "https://placeimg.com/700/300/tech", - "pricing": { - "amount": 368.25, - "currency": "USD" - }, - "description": "Duis anim voluptate est elit reprehenderit excepteur incididunt sunt laboris. Proident ad qui nulla excepteur adipisicing culpa adipisicing do do aliqua. Amet non sunt ipsum adipisicing Lorem ea anim cupidatat aliqua sunt ullamco id aliqua. Anim officia eiusmod culpa laborum dolor minim. Nostrud Lorem non ea sunt officia laborum aute sint aliquip. Consectetur pariatur ut minim quis labore ex enim magna nostrud labore exercitation sunt sunt ullamco. In sit enim aliquip non velit.\r\nLabore commodo ullamco culpa adipisicing ea duis pariatur sit dolor cillum laboris irure excepteur. Id qui ea nostrud veniam fugiat dolor ex ullamco sunt dolore commodo culpa ad. Nostrud dolore pariatur ut velit laboris tempor dolore ipsum. Est cillum ea tempor sint cupidatat ad eiusmod consequat. Cupidatat mollit cupidatat voluptate ex cupidatat esse incididunt sit nostrud est excepteur. Elit ipsum eu ut dolore irure exercitation magna ad commodo proident labore dolore do.\r\n", - "details": [ - "Lorem exercitation excepteur nulla", - "do exercitation nostrud ex", - "officia sint pariatur elit", - "ipsum nostrud esse laboris", - "ut culpa esse exercitation" - ], - "tags": [ - "tag3", - "tag5", - "tag4" - ] - }, - { - "name": "Shepard", - "picture": "https://placeimg.com/700/300/tech", - "pricing": { - "amount": 189.11, - "currency": "USD" - }, - "description": "Deserunt ea sunt est duis fugiat velit laboris. Consequat reprehenderit sunt culpa laborum quis est laborum laboris enim velit proident. Fugiat commodo id mollit tempor duis Lorem commodo nulla exercitation consectetur veniam proident minim.\r\nIn qui eiusmod et esse id enim in culpa veniam Lorem ad pariatur amet et. Non ex magna ex mollit minim ullamco in ipsum est. Ad nostrud minim velit laborum labore. Aliqua magna excepteur incididunt ea sunt eiusmod eiusmod velit enim mollit nulla velit. Ad reprehenderit adipisicing dolor proident nisi nisi ad aliquip dolor nostrud laboris enim sint incididunt. Non velit aliquip mollit commodo. Minim deserunt ex anim sit dolor.\r\n", - "details": [ - "ea voluptate amet ex", - "voluptate qui exercitation quis", - "do duis et magna", - "aliquip velit magna culpa", - "ea velit irure in" - ], - "tags": [ - "tag5", - "tag3", - "tag4" - ] - }, - { - "name": "Ozean", - "picture": "https://placeimg.com/700/300/tech", - "pricing": { - "amount": 56.05, - "currency": "USD" - }, - "description": "Dolore commodo ullamco elit consectetur velit ea. Amet sunt tempor aliquip do tempor. Ea adipisicing cupidatat magna consectetur irure est reprehenderit consequat sint reprehenderit deserunt tempor exercitation ullamco. Consequat enim Lorem nisi duis consectetur aliqua dolor aute ad. Cupidatat in irure excepteur sit.\r\nIrure culpa elit est proident non. Consectetur esse officia do ad fugiat pariatur et fugiat ad esse id officia laboris nisi. Non anim non cupidatat incididunt pariatur voluptate. Ipsum deserunt consequat esse aute et id esse amet nostrud velit mollit. Reprehenderit consectetur aliqua incididunt et quis consequat aute. Consectetur magna est nulla elit incididunt non dolore mollit.\r\n", - "details": [ - "Lorem ut amet in", - "non cupidatat est fugiat", - "aliquip aute irure qui", - "Lorem excepteur consequat fugiat", - "incididunt officia deserunt adipisicing" - ], - "tags": [ - "tag3", - "tag5", - "tag3" - ] - }, - { - "name": "Enjola", - "picture": "https://placeimg.com/700/300/tech", - "pricing": { - "amount": 381.92, - "currency": "USD" - }, - "description": "Exercitation ex exercitation cupidatat eiusmod officia. Irure nostrud occaecat ut quis. Adipisicing Lorem irure deserunt enim non deserunt aliquip Lorem Lorem dolor exercitation sit adipisicing id.\r\nAliquip exercitation culpa proident nulla non ut. Adipisicing ipsum consectetur duis consectetur excepteur nostrud mollit ut ipsum labore magna non et. Cupidatat cupidatat ullamco aliquip in ex sunt aliquip exercitation et velit id Lorem sit. Non consequat elit et deserunt voluptate mollit anim veniam occaecat id. Sint non labore id sit. Cupidatat quis ipsum consectetur eu reprehenderit non ullamco laboris eu. Nulla ut cupidatat est exercitation cillum do pariatur aute ut cupidatat.\r\n", - "details": [ - "anim eu magna sit", - "occaecat proident minim ex", - "sit do esse ipsum", - "ad sint laboris aliqua", - "adipisicing exercitation proident laboris" - ], - "tags": [ - "tag6", - "tag5", - "tag5" - ] - }, - { - "name": "Eclipsent", - "picture": "https://placeimg.com/700/300/tech", - "pricing": { - "amount": 138.51, - "currency": "USD" - }, - "description": "Lorem dolor aliqua irure quis. Ex excepteur sint id adipisicing nisi cillum aliqua amet aute laborum. Commodo reprehenderit ad dolor voluptate consectetur anim. Irure elit qui laboris nulla qui tempor et. Do id mollit cillum et sunt enim ut excepteur laborum qui elit excepteur. Dolore eiusmod consectetur irure labore fugiat sint irure. Elit commodo officia nostrud sit consectetur exercitation in occaecat.\r\nPariatur id duis qui eu eiusmod. Pariatur esse consequat tempor fugiat fugiat aliqua voluptate eu qui nisi est est. Sint magna veniam reprehenderit deserunt dolor minim est dolore cupidatat exercitation incididunt eiusmod tempor aliqua.\r\n", - "details": [ - "deserunt est esse Lorem", - "nulla nisi cupidatat laboris", - "esse aliquip proident do", - "ea incididunt sint adipisicing", - "anim cupidatat nostrud pariatur" - ], - "tags": [ - "tag1", - "tag2", - "tag6" - ] - }, - { - "name": "Flyboyz", - "picture": "https://placeimg.com/700/300/tech", - "pricing": { - "amount": 176.79, - "currency": "USD" - }, - "description": "Pariatur aliquip fugiat cupidatat elit culpa fugiat. Ut culpa id dolore deserunt esse do velit aute aliqua anim irure incididunt id elit. Adipisicing duis labore ea voluptate ea commodo pariatur sint nisi do proident anim labore. Do ullamco aliqua consequat duis ut magna incididunt non et irure sint quis.\r\nCillum esse enim quis ad quis commodo adipisicing proident. Ipsum excepteur laborum veniam ipsum culpa consectetur tempor eu laboris deserunt et tempor. Sit elit dolore in veniam ullamco tempor deserunt officia nulla. Labore qui adipisicing irure anim laboris aute sint officia consectetur Lorem.\r\n", - "details": [ - "in laboris id cupidatat", - "eu voluptate sunt duis", - "mollit sint quis aute", - "tempor aliqua esse proident", - "deserunt aute labore commodo" - ], - "tags": [ - "tag4", - "tag1", - "tag1" - ] - }, - { - "name": "Cofine", - "picture": "https://placeimg.com/700/300/tech", - "pricing": { - "amount": 271.86, - "currency": "USD" - }, - "description": "Ea sint velit pariatur et do duis minim eu consequat ex. Nulla ea id eu fugiat non voluptate nulla pariatur elit dolore proident. Reprehenderit eu pariatur commodo commodo adipisicing veniam qui irure culpa. Est eu consectetur commodo velit proident incididunt proident dolor anim.\r\nOccaecat deserunt occaecat irure velit. Fugiat consectetur minim aliquip nostrud est fugiat eiusmod reprehenderit laboris quis veniam eiusmod in. Duis laboris non velit aute laborum qui aute veniam fugiat dolore. Officia laborum proident sit cupidatat do ullamco officia magna nulla non cillum. Ea ullamco est non velit est pariatur ad adipisicing. Ex nulla consequat exercitation commodo in quis exercitation id commodo ex ea ex laborum eiusmod. Voluptate quis elit Lorem id sint velit esse.\r\n", - "details": [ - "qui ad amet fugiat", - "laboris velit adipisicing amet", - "consequat occaecat irure aliquip", - "veniam ullamco voluptate eu", - "consectetur nulla laboris sunt" - ], - "tags": [ - "tag4", - "tag2", - "tag2" - ] - }, - { - "name": "Senmei", - "picture": "https://placeimg.com/700/300/tech", - "pricing": { - "amount": 77.15, - "currency": "USD" - }, - "description": "Incididunt anim qui ex veniam. Mollit elit esse voluptate sit dolor labore officia do officia cupidatat quis. Amet consectetur ullamco aliquip Lorem veniam enim cillum amet.\r\nDeserunt occaecat ullamco fugiat laborum aliquip eu irure culpa tempor sit irure cillum veniam veniam. Sunt eu eiusmod occaecat do dolore eu laboris esse in. Dolore enim labore qui sit ex minim velit officia dolore consectetur laboris ad dolore incididunt.\r\n", - "details": [ - "amet sunt culpa duis", - "ullamco occaecat reprehenderit proident", - "occaecat incididunt veniam ea", - "aliqua voluptate pariatur labore", - "amet minim tempor aute" - ], - "tags": [ - "tag2", - "tag6", - "tag3" - ] - }, - { - "name": "Accupharm", - "picture": "https://placeimg.com/700/300/tech", - "pricing": { - "amount": 38.2, - "currency": "USD" - }, - "description": "Duis ea irure ad pariatur magna nulla magna voluptate. Aliqua excepteur elit laborum amet dolor aute et commodo laboris elit duis. Incididunt aliquip cupidatat sit mollit. Dolore do ipsum Lorem exercitation deserunt reprehenderit minim ad mollit ut tempor. Nulla proident ullamco exercitation irure laboris aliquip ut exercitation fugiat. Nostrud do adipisicing labore amet dolore fugiat incididunt qui do non ad incididunt cillum.\r\nExercitation in nulla deserunt laborum eu aliqua mollit aliqua. Laborum in incididunt minim mollit excepteur magna veniam magna sit ullamco magna exercitation irure esse. Ullamco consequat ullamco in commodo voluptate reprehenderit proident amet adipisicing veniam ex ipsum deserunt.\r\n", - "details": [ - "adipisicing proident incididunt dolor", - "quis ad consequat duis", - "cillum occaecat adipisicing voluptate", - "id aliqua et cillum", - "est quis elit duis" - ], - "tags": [ - "tag3", - "tag5", - "tag2" - ] - }, - { - "name": "Flumbo", - "picture": "https://placeimg.com/700/300/tech", - "pricing": { - "amount": 304.43, - "currency": "USD" - }, - "description": "Enim exercitation et occaecat minim ullamco sunt officia commodo eu irure sit. Et consequat consequat dolor dolore eiusmod consequat Lorem culpa aliqua occaecat Lorem quis. Mollit dolore consectetur excepteur ex laborum. Mollit Lorem duis nisi ipsum aliqua esse. Qui irure esse in eiusmod do duis amet exercitation voluptate. Voluptate sit aute minim est sunt tempor fugiat in aliquip nisi pariatur esse et.\r\nAd est ullamco id labore ea pariatur eiusmod elit aliqua mollit. Sit culpa labore incididunt adipisicing ullamco est in aliqua proident cillum tempor eiusmod deserunt. Sint reprehenderit amet sint veniam ex nostrud nisi aute. Reprehenderit cillum deserunt consequat tempor ad do incididunt culpa voluptate quis. Commodo nostrud in amet anim voluptate incididunt velit voluptate culpa deserunt esse. Non consectetur mollit veniam aute irure duis amet incididunt Lorem eiusmod non enim dolore. Lorem commodo fugiat ad ex cillum.\r\n", - "details": [ - "magna ullamco ad dolore", - "qui laboris amet adipisicing", - "aliqua eu excepteur nisi", - "enim fugiat eu est", - "id aliquip deserunt amet" - ], - "tags": [ - "tag3", - "tag3", - "tag1" - ] - }, - { - "name": "Eplosion", - "picture": "https://placeimg.com/700/300/tech", - "pricing": { - "amount": 380.43, - "currency": "USD" - }, - "description": "Est excepteur velit voluptate elit duis nostrud commodo dolore enim sit excepteur commodo. Amet sint aute cillum incididunt do sit incididunt elit mollit tempor irure incididunt. Officia esse non sit cupidatat sunt tempor. Lorem do laboris incididunt Lorem ipsum adipisicing commodo labore aute cillum veniam. Magna anim officia ex ad laboris cillum elit fugiat amet eiusmod.\r\nLaborum non pariatur consequat ex ad. Qui ex Lorem velit ex tempor velit occaecat enim in qui mollit in. Eiusmod elit officia incididunt proident exercitation quis labore ad est sint amet exercitation proident. Reprehenderit consequat ipsum veniam cupidatat elit culpa velit reprehenderit irure incididunt amet tempor. Amet consequat ullamco ad qui sint adipisicing deserunt nostrud laborum fugiat ex irure. Labore ad consequat voluptate magna fugiat cillum fugiat Lorem quis ex nulla mollit. Laborum officia veniam pariatur voluptate voluptate do occaecat quis ea elit.\r\n", - "details": [ - "consequat nisi reprehenderit id", - "ad aliqua mollit duis", - "consectetur et in enim", - "id esse eiusmod adipisicing", - "quis quis ad pariatur" - ], - "tags": [ - "tag4", - "tag2", - "tag5" - ] - }, - { - "name": "Blanet", - "picture": "https://placeimg.com/700/300/tech", - "pricing": { - "amount": 309.92, - "currency": "USD" - }, - "description": "Aliqua deserunt excepteur nulla quis ad cupidatat aliquip. Voluptate est irure commodo est do culpa eiusmod duis. Nisi tempor fugiat in velit est nulla anim deserunt. Ullamco est esse laboris nisi dolor nulla. Sunt eiusmod pariatur aliquip sint consequat excepteur velit nostrud exercitation ex. Deserunt anim commodo eu pariatur aliqua nulla ad consequat ipsum fugiat esse labore cillum incididunt.\r\nCommodo est occaecat eiusmod officia pariatur reprehenderit voluptate voluptate. Duis et aliqua ex ex ad incididunt in ad. Anim pariatur do ea irure sit.\r\n", - "details": [ - "sint minim aute est", - "in aliqua laborum tempor", - "elit dolor voluptate amet", - "deserunt quis non proident", - "ex id nisi tempor" - ], - "tags": [ - "tag6", - "tag1", - "tag4" - ] - }, - { - "name": "Xymonk", - "picture": "https://placeimg.com/700/300/tech", - "pricing": { - "amount": 44.46, - "currency": "USD" - }, - "description": "Ipsum minim pariatur sit sunt dolore commodo laborum mollit sunt id duis labore. Ad aute ipsum nulla ut fugiat mollit anim cillum eu. Occaecat in enim Lorem enim id sit. Do cillum cillum nostrud elit voluptate id.\r\nIncididunt nisi proident consectetur amet elit. Cupidatat velit ut quis sunt qui fugiat. Labore laboris cupidatat eiusmod cillum id non et cillum proident dolore. Excepteur commodo ipsum aliquip fugiat. Aliquip Lorem culpa cillum ut nulla aliquip elit nulla aute. Occaecat ea dolore eu eiusmod.\r\n", - "details": [ - "esse sunt minim aute", - "est mollit ea velit", - "ut nostrud officia fugiat", - "officia fugiat cillum eiusmod", - "exercitation excepteur irure reprehenderit" - ], - "tags": [ - "tag2", - "tag6", - "tag1" - ] - }, - { - "name": "Songlines", - "picture": "https://placeimg.com/700/300/tech", - "pricing": { - "amount": 245.91, - "currency": "USD" - }, - "description": "Anim esse dolore id aliquip cillum ipsum. Occaecat irure Lorem quis eu ullamco labore nisi aliquip aute irure dolore. Sit consequat irure qui occaecat laborum. Minim eiusmod dolor sit et dolor deserunt tempor reprehenderit nulla enim elit aliqua magna.\r\nSunt ipsum exercitation irure nulla fugiat consequat eiusmod cupidatat velit nisi. Aliquip ullamco officia adipisicing laborum sit reprehenderit nisi in. Reprehenderit sunt est dolor proident ad duis aliquip velit sit ea in anim elit occaecat. Minim esse voluptate nisi occaecat do adipisicing voluptate eiusmod occaecat sunt. Voluptate voluptate deserunt labore tempor consequat aliquip qui occaecat pariatur cupidatat nulla. Amet velit ea veniam sunt ea ullamco anim. Id excepteur ad eu laboris cillum dolore velit.\r\n", - "details": [ - "cillum pariatur dolore consequat", - "et mollit elit in", - "ipsum tempor officia tempor", - "ipsum exercitation fugiat excepteur", - "eiusmod est officia aute" - ], - "tags": [ - "tag2", - "tag1", - "tag1" - ] - }, - { - "name": "Magnemo", - "picture": "https://placeimg.com/700/300/tech", - "pricing": { - "amount": 62.89, - "currency": "USD" - }, - "description": "Adipisicing nulla est ad aliquip velit id labore aute irure non. Ex id ex deserunt nisi in tempor et mollit est. Consectetur officia in deserunt laborum ex sint et tempor dolore labore sunt. Commodo excepteur in consequat adipisicing cupidatat non velit laborum consequat elit. Irure laboris est culpa ullamco tempor culpa incididunt cillum esse cupidatat. Aliqua deserunt qui consequat pariatur reprehenderit qui eu consectetur occaecat velit minim dolor quis.\r\nMollit cillum aute in nostrud est ut. Est id officia eu sint irure dolore est est Lorem. Anim voluptate in est consectetur aliquip ad nisi reprehenderit sunt dolore culpa anim tempor voluptate. Elit irure ea irure exercitation laboris esse cillum officia est cupidatat officia aliqua culpa. Nulla laborum et ex dolore esse id. Laborum mollit incididunt minim consectetur aliqua et et cillum ipsum enim laboris. Enim et commodo consequat elit consectetur aliqua culpa do nisi eu exercitation.\r\n", - "details": [ - "fugiat non officia cillum", - "voluptate pariatur ut sint", - "et sunt tempor sint", - "sit incididunt sunt amet", - "do elit commodo laboris" - ], - "tags": [ - "tag6", - "tag6", - "tag4" - ] - }, - { - "name": "Isologica", - "picture": "https://placeimg.com/700/300/tech", - "pricing": { - "amount": 304.6, - "currency": "USD" - }, - "description": "Esse Lorem sint reprehenderit elit incididunt est do do officia. Do voluptate aliqua minim magna. Aliqua ipsum cupidatat enim ad veniam. In nulla labore magna aliquip veniam. Nostrud laboris irure non dolor sit voluptate. Dolor in non ut mollit et. Lorem Lorem cillum aliquip in eu quis aliqua do excepteur Lorem cillum dolore aliqua laboris.\r\nCillum excepteur in incididunt Lorem nulla ullamco sunt pariatur consectetur non proident consequat. Aliquip minim sunt dolor culpa reprehenderit est ea culpa veniam eu dolore aliquip. Consequat occaecat Lorem in amet. Laboris anim elit amet ut do ullamco voluptate culpa. Non cupidatat tempor consequat excepteur. Cillum mollit culpa aliqua ullamco non elit commodo voluptate sit eu ad.\r\n", - "details": [ - "nulla laborum eu et", - "ipsum commodo nulla occaecat", - "labore consequat duis cillum", - "culpa anim aute occaecat", - "veniam minim culpa sint" - ], - "tags": [ - "tag2", - "tag1", - "tag1" - ] - }, - { - "name": "Quotezart", - "picture": "https://placeimg.com/700/300/tech", - "pricing": { - "amount": 372.9, - "currency": "USD" - }, - "description": "Reprehenderit ipsum irure duis ut cillum officia anim aute duis ullamco consectetur est. Commodo ad fugiat excepteur laborum proident ad. Est reprehenderit cupidatat nisi nostrud. Voluptate eu voluptate consequat anim. Anim voluptate officia magna labore nulla incididunt nulla.\r\nNulla aliquip anim Lorem cupidatat nostrud occaecat labore magna sint ex pariatur magna. Tempor cupidatat Lorem est veniam. In ullamco sit velit irure dolor mollit deserunt. Cupidatat labore in incididunt consectetur ea quis voluptate qui. Et ad dolor laborum veniam incididunt fugiat adipisicing velit aute reprehenderit dolor officia non sit.\r\n", - "details": [ - "aliquip Lorem dolore nulla", - "do sit anim dolore", - "aliqua culpa ut eiusmod", - "officia laborum magna non", - "dolor Lorem cillum eu" - ], - "tags": [ - "tag3", - "tag6", - "tag5" - ] - }, - { - "name": "Bittor", - "picture": "https://placeimg.com/700/300/tech", - "pricing": { - "amount": 414.04, - "currency": "USD" - }, - "description": "Commodo quis fugiat ad ut ex in anim reprehenderit aute. Nisi enim amet sint ipsum laboris adipisicing eu dolore quis aute. Culpa in laborum qui ea mollit pariatur sit cupidatat occaecat tempor aliquip occaecat Lorem enim. Ea reprehenderit duis amet do velit officia in nisi enim ea sint fugiat.\r\nNisi minim irure tempor proident elit nulla id nisi duis quis laboris consectetur ad id. Eu reprehenderit voluptate veniam quis enim adipisicing dolor ut eiusmod cupidatat minim deserunt. Occaecat in nisi excepteur cupidatat et nulla qui exercitation aliquip culpa.\r\n", - "details": [ - "culpa dolor ut laborum", - "duis irure laboris eu", - "deserunt laboris aliqua adipisicing", - "qui qui cillum amet", - "reprehenderit laboris reprehenderit elit" - ], - "tags": [ - "tag1", - "tag4", - "tag2" - ] - }, - { - "name": "Ziore", - "picture": "https://placeimg.com/700/300/tech", - "pricing": { - "amount": 452.04, - "currency": "USD" - }, - "description": "Quis magna officia officia do. In in sit mollit esse ipsum sit aliqua quis id et ex sint elit. Cupidatat aute proident anim culpa ullamco et et dolor do laboris anim cillum aute. Do reprehenderit laboris sit velit ex ullamco est velit ut in non irure nulla. Dolor ex ea officia dolore nostrud qui Lorem ea proident tempor.\r\nAliqua amet voluptate dolore labore occaecat anim qui velit occaecat tempor magna tempor anim ea. Enim velit excepteur qui laboris esse duis pariatur non nostrud. Nostrud enim reprehenderit aute aliqua laborum cillum officia dolore sit tempor aliqua incididunt. Officia voluptate veniam ipsum sunt nostrud officia ad duis voluptate. Qui pariatur consectetur do veniam velit amet eiusmod ad commodo reprehenderit. Lorem aute laboris nulla consectetur sit fugiat dolore ipsum.\r\n", - "details": [ - "aute culpa reprehenderit qui", - "ad irure non culpa", - "anim reprehenderit sit labore", - "irure et nulla aliquip", - "exercitation elit proident laborum" - ], - "tags": [ - "tag1", - "tag6", - "tag4" - ] - }, - { - "name": "Zaggles", - "picture": "https://placeimg.com/700/300/tech", - "pricing": { - "amount": 437.02, - "currency": "USD" - }, - "description": "Incididunt aliqua sint laboris veniam Lorem enim sunt reprehenderit. Enim magna qui excepteur sit velit cillum occaecat fugiat minim nulla anim occaecat. Nisi ad voluptate adipisicing enim eiusmod enim excepteur magna enim commodo.\r\nNon excepteur non ea aute ad dolore eiusmod est Lorem non tempor. Id pariatur reprehenderit cupidatat aute incididunt incididunt mollit non labore. Qui ea do amet sint voluptate tempor aliquip adipisicing veniam cillum irure exercitation. Ea et non aliquip quis magna Lorem ipsum dolore consectetur nostrud. Mollit deserunt incididunt ullamco sunt quis mollit excepteur et voluptate incididunt sunt. Lorem commodo sint laboris incididunt.\r\n", - "details": [ - "sunt veniam amet sit", - "sit mollit nisi in", - "deserunt dolor exercitation est", - "consequat aute consectetur proident", - "deserunt veniam est consectetur" - ], - "tags": [ - "tag3", - "tag6", - "tag1" - ] - }, - { - "name": "Pivitol", - "picture": "https://placeimg.com/700/300/tech", - "pricing": { - "amount": 423.75, - "currency": "USD" - }, - "description": "Irure aliqua incididunt est exercitation Lorem. Voluptate eiusmod occaecat ullamco proident sint minim ex laboris anim. Sunt sint labore reprehenderit ipsum quis exercitation esse ea tempor occaecat ipsum. Aliqua aute culpa cillum consectetur ipsum. Excepteur esse pariatur sit ipsum nostrud anim eu. Ad ipsum dolore anim deserunt aliqua officia consectetur. Occaecat incididunt non labore excepteur aliquip adipisicing reprehenderit aute ex occaecat.\r\nIpsum irure fugiat ea velit sunt commodo qui. Ex voluptate quis anim ex exercitation in sunt aliqua in elit. Sint ut ut adipisicing duis anim voluptate aliquip. Ipsum dolore eu ullamco tempor nostrud reprehenderit est.\r\n", - "details": [ - "irure consequat duis pariatur", - "cupidatat ea elit ipsum", - "sint nostrud tempor quis", - "eiusmod pariatur et aute", - "voluptate ullamco elit qui" - ], - "tags": [ - "tag3", - "tag6", - "tag3" - ] - }, - { - "name": "Kozgene", - "picture": "https://placeimg.com/700/300/tech", - "pricing": { - "amount": 231.32, - "currency": "USD" - }, - "description": "Ad labore enim eiusmod labore nisi do ipsum enim culpa. Ipsum deserunt non aliqua est consectetur esse minim. Ut sunt culpa enim laborum adipisicing elit. Elit esse laboris pariatur Lorem ea dolore proident cillum aliquip ex adipisicing irure.\r\nEiusmod sunt magna voluptate ullamco. Incididunt aute ex nulla ipsum cillum nisi consectetur voluptate amet dolore aute nulla amet exercitation. Est ipsum ut ipsum ex pariatur excepteur est eu ut Lorem eiusmod mollit minim eu. Veniam laboris laboris minim occaecat dolor officia occaecat dolore ipsum id laboris id ut. Do aliqua fugiat velit enim anim mollit reprehenderit aute.\r\n", - "details": [ - "id sit commodo elit", - "sunt sint ipsum incididunt", - "in occaecat ipsum in", - "ullamco anim fugiat quis", - "ullamco dolore minim occaecat" - ], - "tags": [ - "tag3", - "tag3", - "tag6" - ] - }, - { - "name": "Animalia", - "picture": "https://placeimg.com/700/300/tech", - "pricing": { - "amount": 120.93, - "currency": "USD" - }, - "description": "Duis magna ullamco ut ex. Nostrud Lorem ad duis proident nostrud non culpa. Minim pariatur tempor aute dolor et non elit cillum. Eiusmod nisi labore do anim ad. Nisi labore amet reprehenderit et adipisicing consequat Lorem nisi cillum magna tempor sint aute non. In irure ipsum non ad tempor. Reprehenderit aliqua esse fugiat magna id veniam sit eu enim tempor magna incididunt non ut.\r\nDuis occaecat cupidatat nisi et aute mollit Lorem fugiat amet sunt adipisicing. Ea id eiusmod dolor proident sit aliqua. Quis eiusmod id pariatur pariatur commodo mollit excepteur nostrud. Adipisicing duis ad sunt qui ullamco exercitation et aliqua magna eu do ut veniam. Exercitation esse dolore magna Lorem consectetur ex aliquip officia reprehenderit non voluptate in dolore esse.\r\n", - "details": [ - "ex occaecat non cupidatat", - "duis enim laborum esse", - "ullamco et consequat sit", - "exercitation ipsum sunt cupidatat", - "dolor quis ut velit" - ], - "tags": [ - "tag5", - "tag5", - "tag2" - ] - }, - { - "name": "Zillanet", - "picture": "https://placeimg.com/700/300/tech", - "pricing": { - "amount": 411.85, - "currency": "USD" - }, - "description": "Laboris id reprehenderit velit minim irure proident id excepteur laborum proident. Fugiat cupidatat velit proident non. Ut nisi deserunt tempor excepteur adipisicing non excepteur consequat tempor consequat Lorem.\r\nAd eu dolore velit laborum. Sint do nulla amet voluptate officia velit qui do Lorem do reprehenderit magna. Eu consectetur veniam elit dolor laboris id do proident aliquip nisi in do veniam. Elit eu dolor amet duis aliquip anim consectetur nostrud et ea tempor. Eu commodo occaecat aute ipsum dolore mollit. Enim nulla esse officia officia.\r\n", - "details": [ - "incididunt magna deserunt Lorem", - "esse veniam tempor nulla", - "ea sint elit labore", - "nostrud officia voluptate consectetur", - "adipisicing mollit labore dolore" - ], - "tags": [ - "tag1", - "tag2", - "tag6" - ] - }, - { - "name": "Comvex", - "picture": "https://placeimg.com/700/300/tech", - "pricing": { - "amount": 396.37, - "currency": "USD" - }, - "description": "Velit ea consectetur quis qui pariatur ex officia qui sint non eiusmod. Dolore non laborum anim aliquip culpa veniam eu fugiat. Nostrud tempor aliquip Lorem sunt mollit excepteur dolor ut nostrud exercitation id qui.\r\nLaboris culpa sint aliqua dolor ex dolore est reprehenderit ad qui voluptate quis. Reprehenderit eiusmod irure aliquip do nulla voluptate aliquip minim do eiusmod id commodo ullamco. Id veniam eiusmod ea excepteur veniam ipsum aliquip elit do nulla. Velit commodo exercitation quis irure dolore nostrud. Do consequat incididunt exercitation ex commodo dolore sit enim minim eiusmod minim veniam et. Consectetur ullamco anim duis commodo id reprehenderit. Ex officia nisi ut reprehenderit.\r\n", - "details": [ - "officia esse consectetur amet", - "occaecat adipisicing laboris quis", - "voluptate in velit labore", - "proident est incididunt fugiat", - "elit consectetur consectetur quis" - ], - "tags": [ - "tag5", - "tag6", - "tag6" - ] - }, - { - "name": "Netplode", - "picture": "https://placeimg.com/700/300/tech", - "pricing": { - "amount": 175.4, - "currency": "USD" - }, - "description": "Minim ipsum id nostrud sunt proident adipisicing quis ad ipsum quis enim labore eiusmod. Excepteur sint do sit ex fugiat pariatur ipsum adipisicing consectetur laborum ea pariatur. Ut ullamco cillum eiusmod nostrud aliquip non dolor occaecat.\r\nExcepteur aliquip aute ea proident dolore qui mollit proident cupidatat ut. Proident fugiat ullamco velit elit tempor nostrud laborum magna est cillum. Ea sint reprehenderit pariatur id velit eu id ut nisi irure.\r\n", - "details": [ - "mollit voluptate proident do", - "labore ex veniam anim", - "nulla irure consequat reprehenderit", - "duis id amet consequat", - "proident eu deserunt id" - ], - "tags": [ - "tag1", - "tag2", - "tag6" - ] - }, - { - "name": "Icology", - "picture": "https://placeimg.com/700/300/tech", - "pricing": { - "amount": 433.97, - "currency": "USD" - }, - "description": "Pariatur dolor eu Lorem cillum occaecat do. Fugiat incididunt aliqua aute voluptate deserunt dolore Lorem dolor duis pariatur aliqua ex velit. Nulla ut veniam duis dolore sint nostrud. Incididunt quis Lorem ullamco veniam mollit aliqua dolor laborum. Ullamco voluptate esse laborum esse exercitation. Esse tempor quis velit nostrud nulla magna in irure ad. Ea esse tempor veniam veniam pariatur dolor ut magna amet irure.\r\nConsectetur deserunt quis excepteur anim consectetur minim mollit quis ad excepteur cillum labore laboris. Id ipsum nostrud aliquip voluptate ad exercitation tempor cillum tempor eu. Est pariatur exercitation ut cupidatat culpa dolore Lorem occaecat id mollit. Enim tempor commodo exercitation mollit consequat do sit reprehenderit labore ut enim non occaecat. Irure culpa incididunt nisi incididunt. Ea aliqua cupidatat ipsum eiusmod consectetur culpa esse consectetur laboris ad duis.\r\n", - "details": [ - "ipsum reprehenderit adipisicing sit", - "laboris do ullamco consectetur", - "consequat consectetur sit cillum", - "dolore laboris consequat non", - "cupidatat nisi ea qui" - ], - "tags": [ - "tag6", - "tag6", - "tag5" - ] - }, - { - "name": "Bullzone", - "picture": "https://placeimg.com/700/300/tech", - "pricing": { - "amount": 316.33, - "currency": "USD" - }, - "description": "Ut nisi irure aliquip excepteur. Ea eu laborum non sunt ad irure est amet nulla nulla cillum. Ipsum dolore magna culpa est irure mollit tempor nisi reprehenderit incididunt fugiat aliquip.\r\nConsectetur eiusmod exercitation sit dolor amet sit commodo nostrud. Veniam nisi ut ullamco nisi id duis magna tempor officia. Elit reprehenderit est est voluptate aute amet ipsum elit laborum irure Lorem fugiat dolore. Eiusmod proident magna duis tempor dolore fugiat nostrud nostrud mollit.\r\n", - "details": [ - "laboris Lorem amet nisi", - "veniam minim esse non", - "exercitation ex deserunt nisi", - "in aliquip ex in", - "aliqua sint est tempor" - ], - "tags": [ - "tag1", - "tag3", - "tag4" - ] - }, - { - "name": "Evidends", - "picture": "https://placeimg.com/700/300/tech", - "pricing": { - "amount": 108.01, - "currency": "USD" - }, - "description": "Incididunt laborum anim Lorem quis amet veniam qui id. Anim amet voluptate officia irure exercitation velit esse voluptate adipisicing. Do reprehenderit adipisicing consequat culpa aliqua officia sint consectetur. Id excepteur deserunt sunt excepteur nostrud consectetur veniam quis incididunt. Mollit cillum ipsum reprehenderit proident anim minim reprehenderit in consectetur nisi. Culpa sit culpa ullamco sit excepteur culpa nulla in dolore deserunt eiusmod aute labore.\r\nElit proident quis tempor elit officia dolor qui ad sunt deserunt qui. Amet commodo nulla velit in est laborum laborum ex pariatur occaecat cupidatat exercitation. Est nostrud excepteur nostrud consequat cillum incididunt officia minim ullamco. Esse ut reprehenderit est do ipsum laborum laborum excepteur veniam tempor laboris ullamco incididunt dolore.\r\n", - "details": [ - "consequat culpa eu veniam", - "mollit laboris sunt nisi", - "sint anim deserunt culpa", - "anim eiusmod consequat mollit", - "sit culpa qui qui" - ], - "tags": [ - "tag1", - "tag6", - "tag6" - ] - }, - { - "name": "Zilidium", - "picture": "https://placeimg.com/700/300/tech", - "pricing": { - "amount": 92.9, - "currency": "USD" - }, - "description": "Ea amet id irure dolor eu ex nulla proident esse voluptate exercitation adipisicing. Commodo laboris voluptate cupidatat quis do quis aute in mollit. Veniam aliqua mollit velit magna est pariatur incididunt nulla ex sit adipisicing. Anim in ad aute est excepteur consequat excepteur occaecat.\r\nDuis dolore sint officia cillum cillum minim sint aliqua qui qui consequat Lorem minim. Ullamco officia id minim sunt commodo voluptate. Excepteur aliqua incididunt labore anim sit enim incididunt irure nisi sunt. Et magna laboris excepteur aute cillum.\r\n", - "details": [ - "ea nostrud culpa do", - "irure quis consequat dolor", - "enim officia dolore id", - "eiusmod minim sunt non", - "dolore et ad ipsum" - ], - "tags": [ - "tag4", - "tag5", - "tag2" - ] - } - ] - } -] \ No newline at end of file diff --git a/full-apps/catalog-microservice/src/main/resources/application.yaml b/full-apps/catalog-microservice/src/main/resources/application.yaml deleted file mode 100644 index 9612c3a..0000000 --- a/full-apps/catalog-microservice/src/main/resources/application.yaml +++ /dev/null @@ -1,41 +0,0 @@ -# -# Copyright © 2013-2018, The SeedStack authors -# -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. -# - -application: - id: catalog - -logging: INFO - -jdbc: - datasources: - catalog: - url: jdbc:hsqldb:mem:catalog - -jpa: - units: - catalogDomain: - datasource: catalog - properties: - hibernate.dialect: org.hibernate.dialect.HSQLDialect - hibernate.hbm2ddl.auto: update - -classes: - org: - seedstack: - samples: - catalog: - domain: - jpaUnit: catalogDomain - -web: - server: - port: ${env.PORT} - cors: - enabled: true - properties: - supportedMethods: GET, POST, HEAD, OPTIONS, PUT, DELETE diff --git a/full-apps/catalog-microservice/src/test/java/org/seedstack/samples/catalog/ProductAssemblerIT.java b/full-apps/catalog-microservice/src/test/java/org/seedstack/samples/catalog/ProductAssemblerIT.java deleted file mode 100644 index da5a5ad..0000000 --- a/full-apps/catalog-microservice/src/test/java/org/seedstack/samples/catalog/ProductAssemblerIT.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright © 2013-2018, The SeedStack authors - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ - -package org.seedstack.samples.catalog; - -import javax.inject.Inject; -import org.assertj.core.api.Assertions; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.seedstack.business.assembler.FluentAssembler; -import org.seedstack.samples.catalog.domain.model.product.Product; -import org.seedstack.samples.catalog.interfaces.rest.product.ProductRepresentation; -import org.seedstack.seed.rest.hal.HalRepresentation; -import org.seedstack.seed.rest.hal.Link; -import org.seedstack.seed.testing.junit4.internal.JUnit4Runner; - -@RunWith(JUnit4Runner.class) -public class ProductAssemblerIT { - @Inject - private FluentAssembler fluently; - - @Test - public void test_assemble() { - Product product = new Product("productName"); - HalRepresentation representation = fluently.assemble(product).to(ProductRepresentation.class); - - Assertions.assertThat(representation).isNotNull(); - Assertions.assertThat(((Link) representation.getLink("self")).getHref()).isEqualTo("/products/productName"); - Assertions.assertThat(((Link) representation.getLink("tags")).getHref()) - .isEqualTo("/products/productName/tags"); - } -} diff --git a/full-apps/catalog-microservice/src/test/java/org/seedstack/samples/catalog/ProductJpaFinderIT.java b/full-apps/catalog-microservice/src/test/java/org/seedstack/samples/catalog/ProductJpaFinderIT.java deleted file mode 100644 index 95f8ea2..0000000 --- a/full-apps/catalog-microservice/src/test/java/org/seedstack/samples/catalog/ProductJpaFinderIT.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright © 2013-2018, The SeedStack authors - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ - -package org.seedstack.samples.catalog; - -import javax.inject.Inject; -import org.assertj.core.api.Assertions; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.seedstack.business.finder.Range; -import org.seedstack.business.finder.Result; -import org.seedstack.business.view.Page; -import org.seedstack.business.view.PaginatedView; -import org.seedstack.samples.catalog.interfaces.rest.catalog.ProductsFinder; -import org.seedstack.samples.catalog.interfaces.rest.product.ProductRepresentation; -import org.seedstack.seed.testing.junit4.internal.JUnit4Runner; - -@RunWith(JUnit4Runner.class) -public class ProductJpaFinderIT { - - @Inject - private ProductsFinder productsFinder; - - @Test - public void test_find() { - Result result = productsFinder.find(new Range(0, 10), null); - Assertions.assertThat(result).isNotNull(); - Assertions.assertThat(result.getFullSize()).isGreaterThanOrEqualTo(0); - Assertions.assertThat(result.getSize()).isGreaterThanOrEqualTo(0); - } - - @Test - public void test_find_with_filter() { - PaginatedView view = productsFinder.findProducts(new Page(0, 10), "ixa"); - Assertions.assertThat(view).isNotNull(); - Assertions.assertThat(view.getResultSize()).isEqualTo(1); - Assertions.assertThat(view.getView()).hasSize(1); - } -} diff --git a/full-apps/catalog-microservice/src/test/java/org/seedstack/samples/catalog/ProductRepositoryIT.java b/full-apps/catalog-microservice/src/test/java/org/seedstack/samples/catalog/ProductRepositoryIT.java deleted file mode 100644 index f78f8ee..0000000 --- a/full-apps/catalog-microservice/src/test/java/org/seedstack/samples/catalog/ProductRepositoryIT.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright © 2013-2018, The SeedStack authors - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ - -package org.seedstack.samples.catalog; - -import javax.inject.Inject; -import org.assertj.core.api.Assertions; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.seedstack.business.domain.LegacyRepository; -import org.seedstack.jpa.Jpa; -import org.seedstack.jpa.JpaUnit; -import org.seedstack.samples.catalog.application.Config; -import org.seedstack.samples.catalog.domain.model.product.Price; -import org.seedstack.samples.catalog.domain.model.product.Product; -import org.seedstack.seed.testing.junit4.internal.JUnit4Runner; -import org.seedstack.seed.transaction.Transactional; - -@Transactional -@JpaUnit(Config.JPA_UNIT) -@RunWith(JUnit4Runner.class) -public class ProductRepositoryIT { - private static final String PRODUCT_NAME = "SeedStack in Action"; - - @Inject - @Jpa - private LegacyRepository repository; - - @Test - public void test_database_config() { - Product product = new Product(PRODUCT_NAME); - product.setDescription("Book presenting seedstack and all its awesome features"); - product.setPricing(new Price(45, "euro")); - repository.persist(product); - - Product product1 = repository.load(PRODUCT_NAME); - Assertions.assertThat(product1).isNotNull(); - Assertions.assertThat(product1.getDescription()) - .isEqualTo("Book presenting seedstack and all its awesome features"); - - repository.delete(product1); // cleanup - } - - @Test - public void test_related_products() { - Product eventage = repository.load("Xixan"); - Assertions.assertThat(eventage.getRelated()).hasSize(4); - Assertions.assertThat(repository.load(eventage.getRelated().iterator().next())).isNotNull(); - } -} diff --git a/full-apps/catalog-microservice/src/test/java/org/seedstack/samples/catalog/ProductsResourceIT.java b/full-apps/catalog-microservice/src/test/java/org/seedstack/samples/catalog/ProductsResourceIT.java deleted file mode 100644 index 7dcf7fd..0000000 --- a/full-apps/catalog-microservice/src/test/java/org/seedstack/samples/catalog/ProductsResourceIT.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright © 2013-2018, The SeedStack authors - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ - -package org.seedstack.samples.catalog; - -import static io.restassured.RestAssured.expect; - -import io.restassured.response.Response; -import org.assertj.core.api.Assertions; -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.seedstack.seed.Configuration; -import org.seedstack.seed.testing.junit4.internal.JUnit4Runner; -import org.seedstack.seed.undertow.LaunchWithUndertow; -import org.skyscreamer.jsonassert.JSONAssert; - -@RunWith(JUnit4Runner.class) -@LaunchWithUndertow -public class ProductsResourceIT { - @Configuration("web.runtime.baseUrl") - private String baseUrl; - - @Test - public void hal_builder() throws JSONException { - Response response = expect().statusCode(200).given().header("Content-Type", "application/hal+json") - .get(baseUrl + "products?pageSize=10"); - - JSONObject expectedResponse = expectedResponse(); - - JSONAssert.assertEquals(expectedResponse, new JSONObject(response.asString()), false); - } - - private JSONObject expectedResponse() throws JSONException { - JSONObject obj = new JSONObject(); - // FIXME links no more include query parameters due to the missing support for BeanParam in HAL - // uncomment when the support will be added - //expectedLinks(obj); - - JSONObject embedded = new JSONObject(); - JSONArray products = new JSONArray(); - for (int i = 0; i < 10; i++) { - products.put(new JSONObject()); - } - embedded.put("products", products); - obj.put("_embedded", embedded); - return obj; - } - - @Test - public void json_home() { - Response response = expect().statusCode(200).given().header("Content-Type", "application/json-home") - .get(baseUrl); - - Assertions.assertThat(response.asString()).isNotEmpty(); - } - - @Test - public void validate_pagination() { - expect().statusCode(400).given().header("Content-Type", "application/hal+json") - .get(baseUrl + "products?pageSize=0"); - - expect().statusCode(400).given().header("Content-Type", "application/hal+json") - .get(baseUrl + "products?pageIndex=-1"); - } - - private void expectedLinks(JSONObject obj) throws JSONException { - JSONObject links = new JSONObject(); - links.put("self", new JSONObject().put("href", "/products?pageIndex=0&pageSize=10")); - links.put("next", new JSONObject().put("href", "/products?pageIndex=1&pageSize=10")); - obj.put("_links", links); - } -} diff --git a/full-apps/catalog-microservice/src/test/java/org/seedstack/samples/catalog/TagJpaFinderIT.java b/full-apps/catalog-microservice/src/test/java/org/seedstack/samples/catalog/TagJpaFinderIT.java deleted file mode 100644 index fa91e47..0000000 --- a/full-apps/catalog-microservice/src/test/java/org/seedstack/samples/catalog/TagJpaFinderIT.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright © 2013-2018, The SeedStack authors - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ - -package org.seedstack.samples.catalog; - -import javax.inject.Inject; -import javax.inject.Named; -import org.assertj.core.api.Assertions; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.seedstack.business.finder.Range; -import org.seedstack.business.finder.Result; -import org.seedstack.samples.catalog.interfaces.rest.product.ProductRepresentation; -import org.seedstack.samples.catalog.interfaces.rest.tags.TagFinder; -import org.seedstack.seed.testing.junit4.internal.JUnit4Runner; - -@RunWith(JUnit4Runner.class) -public class TagJpaFinderIT { - - @Inject - @Named("tag") - private TagFinder tagFinder; - - @Test - public void test_find() { - Result result = tagFinder.find(new Range(0, 10), "tag2"); - Assertions.assertThat(result).isNotNull(); - Assertions.assertThat(result.getFullSize()).isGreaterThanOrEqualTo(0); - Assertions.assertThat(result.getSize()).isGreaterThanOrEqualTo(0); - } -} diff --git a/full-apps/ddd/pom.xml b/full-apps/ddd/pom.xml index 71754dd..2788b09 100644 --- a/full-apps/ddd/pom.xml +++ b/full-apps/ddd/pom.xml @@ -15,8 +15,8 @@ org.seedstack.samples samples - 18.4 - ../.. + 20.11-SNAPSHOT + ../../pom.xml ddd-sample @@ -40,9 +40,8 @@ - org.seedstack - web-composite - pom + org.seedstack.seed + seed-rest-jersey2 org.seedstack.seed diff --git a/full-apps/ddd/src/main/java/org/seedstack/samples/ddd/application/services/impl/CargoInspectionServiceImpl.java b/full-apps/ddd/src/main/java/org/seedstack/samples/ddd/application/services/impl/CargoInspectionServiceImpl.java index e998f03..594b9a5 100644 --- a/full-apps/ddd/src/main/java/org/seedstack/samples/ddd/application/services/impl/CargoInspectionServiceImpl.java +++ b/full-apps/ddd/src/main/java/org/seedstack/samples/ddd/application/services/impl/CargoInspectionServiceImpl.java @@ -10,7 +10,8 @@ import java.util.Optional; import javax.inject.Inject; -import org.apache.commons.lang.Validate; + +import org.apache.commons.lang3.Validate; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.seedstack.business.domain.Repository; diff --git a/full-apps/ddd/src/main/java/org/seedstack/samples/ddd/domain/model/cargo/Cargo.java b/full-apps/ddd/src/main/java/org/seedstack/samples/ddd/domain/model/cargo/Cargo.java index 1815d3e..75b01eb 100644 --- a/full-apps/ddd/src/main/java/org/seedstack/samples/ddd/domain/model/cargo/Cargo.java +++ b/full-apps/ddd/src/main/java/org/seedstack/samples/ddd/domain/model/cargo/Cargo.java @@ -8,9 +8,9 @@ package org.seedstack.samples.ddd.domain.model.cargo; -import org.apache.commons.lang.Validate; -import org.mongodb.morphia.annotations.Entity; -import org.mongodb.morphia.annotations.Id; +import dev.morphia.annotations.Entity; +import dev.morphia.annotations.Id; +import org.apache.commons.lang3.Validate; import org.seedstack.business.domain.BaseAggregateRoot; import org.seedstack.business.domain.Identity; import org.seedstack.samples.ddd.domain.model.handling.HandlingEvent; diff --git a/full-apps/ddd/src/main/java/org/seedstack/samples/ddd/domain/model/cargo/Delivery.java b/full-apps/ddd/src/main/java/org/seedstack/samples/ddd/domain/model/cargo/Delivery.java index 6edb334..6fa3eaf 100644 --- a/full-apps/ddd/src/main/java/org/seedstack/samples/ddd/domain/model/cargo/Delivery.java +++ b/full-apps/ddd/src/main/java/org/seedstack/samples/ddd/domain/model/cargo/Delivery.java @@ -19,8 +19,9 @@ import java.util.Date; import java.util.Iterator; -import org.apache.commons.lang.Validate; -import org.mongodb.morphia.annotations.Embedded; + +import dev.morphia.annotations.Embedded; +import org.apache.commons.lang3.Validate; import org.seedstack.business.domain.BaseValueObject; import org.seedstack.samples.ddd.domain.model.handling.HandlingEvent; import org.seedstack.samples.ddd.domain.model.handling.HandlingHistory; diff --git a/full-apps/ddd/src/main/java/org/seedstack/samples/ddd/domain/model/cargo/HandlingActivity.java b/full-apps/ddd/src/main/java/org/seedstack/samples/ddd/domain/model/cargo/HandlingActivity.java index eb3a475..ac57c3a 100644 --- a/full-apps/ddd/src/main/java/org/seedstack/samples/ddd/domain/model/cargo/HandlingActivity.java +++ b/full-apps/ddd/src/main/java/org/seedstack/samples/ddd/domain/model/cargo/HandlingActivity.java @@ -8,8 +8,8 @@ package org.seedstack.samples.ddd.domain.model.cargo; -import org.apache.commons.lang.Validate; -import org.mongodb.morphia.annotations.Embedded; +import dev.morphia.annotations.Embedded; +import org.apache.commons.lang3.Validate; import org.seedstack.business.domain.BaseValueObject; import org.seedstack.samples.ddd.domain.model.handling.HandlingEvent; import org.seedstack.samples.ddd.domain.model.location.UnLocode; diff --git a/full-apps/ddd/src/main/java/org/seedstack/samples/ddd/domain/model/cargo/Itinerary.java b/full-apps/ddd/src/main/java/org/seedstack/samples/ddd/domain/model/cargo/Itinerary.java index 16fd8ba..5639d3a 100644 --- a/full-apps/ddd/src/main/java/org/seedstack/samples/ddd/domain/model/cargo/Itinerary.java +++ b/full-apps/ddd/src/main/java/org/seedstack/samples/ddd/domain/model/cargo/Itinerary.java @@ -11,8 +11,9 @@ import java.util.Collections; import java.util.Date; import java.util.List; -import org.apache.commons.lang.Validate; -import org.mongodb.morphia.annotations.Embedded; + +import dev.morphia.annotations.Embedded; +import org.apache.commons.lang3.Validate; import org.seedstack.business.domain.BaseValueObject; import org.seedstack.samples.ddd.domain.model.handling.HandlingEvent; import org.seedstack.samples.ddd.domain.model.location.Location; diff --git a/full-apps/ddd/src/main/java/org/seedstack/samples/ddd/domain/model/cargo/Leg.java b/full-apps/ddd/src/main/java/org/seedstack/samples/ddd/domain/model/cargo/Leg.java index c190587..fb5af25 100644 --- a/full-apps/ddd/src/main/java/org/seedstack/samples/ddd/domain/model/cargo/Leg.java +++ b/full-apps/ddd/src/main/java/org/seedstack/samples/ddd/domain/model/cargo/Leg.java @@ -9,8 +9,9 @@ package org.seedstack.samples.ddd.domain.model.cargo; import java.util.Date; -import org.apache.commons.lang.Validate; -import org.mongodb.morphia.annotations.Embedded; + +import dev.morphia.annotations.Embedded; +import org.apache.commons.lang3.Validate; import org.seedstack.business.domain.BaseValueObject; import org.seedstack.samples.ddd.domain.model.location.UnLocode; import org.seedstack.samples.ddd.domain.model.voyage.VoyageNumber; diff --git a/full-apps/ddd/src/main/java/org/seedstack/samples/ddd/domain/model/cargo/RouteSpecification.java b/full-apps/ddd/src/main/java/org/seedstack/samples/ddd/domain/model/cargo/RouteSpecification.java index 933a395..261516c 100644 --- a/full-apps/ddd/src/main/java/org/seedstack/samples/ddd/domain/model/cargo/RouteSpecification.java +++ b/full-apps/ddd/src/main/java/org/seedstack/samples/ddd/domain/model/cargo/RouteSpecification.java @@ -12,7 +12,8 @@ import static com.google.common.base.Preconditions.checkState; import java.util.Date; -import org.mongodb.morphia.annotations.Embedded; + +import dev.morphia.annotations.Embedded; import org.seedstack.business.domain.BaseValueObject; import org.seedstack.business.specification.Specification; import org.seedstack.samples.ddd.domain.model.location.UnLocode; diff --git a/full-apps/ddd/src/main/java/org/seedstack/samples/ddd/domain/model/cargo/TrackingId.java b/full-apps/ddd/src/main/java/org/seedstack/samples/ddd/domain/model/cargo/TrackingId.java index d730805..9e5c83e 100644 --- a/full-apps/ddd/src/main/java/org/seedstack/samples/ddd/domain/model/cargo/TrackingId.java +++ b/full-apps/ddd/src/main/java/org/seedstack/samples/ddd/domain/model/cargo/TrackingId.java @@ -8,8 +8,8 @@ package org.seedstack.samples.ddd.domain.model.cargo; -import org.apache.commons.lang.Validate; -import org.mongodb.morphia.annotations.Embedded; +import dev.morphia.annotations.Embedded; +import org.apache.commons.lang3.Validate; import org.seedstack.business.domain.BaseValueObject; /** diff --git a/full-apps/ddd/src/main/java/org/seedstack/samples/ddd/domain/model/handling/HandlingEvent.java b/full-apps/ddd/src/main/java/org/seedstack/samples/ddd/domain/model/handling/HandlingEvent.java index 77eb5e0..660f3e9 100644 --- a/full-apps/ddd/src/main/java/org/seedstack/samples/ddd/domain/model/handling/HandlingEvent.java +++ b/full-apps/ddd/src/main/java/org/seedstack/samples/ddd/domain/model/handling/HandlingEvent.java @@ -10,9 +10,10 @@ import java.util.Date; import java.util.UUID; -import org.apache.commons.lang.Validate; -import org.mongodb.morphia.annotations.Entity; -import org.mongodb.morphia.annotations.Id; + +import dev.morphia.annotations.Entity; +import dev.morphia.annotations.Id; +import org.apache.commons.lang3.Validate; import org.seedstack.business.domain.BaseAggregateRoot; import org.seedstack.business.domain.DomainEvent; import org.seedstack.business.domain.Identity; diff --git a/full-apps/ddd/src/main/java/org/seedstack/samples/ddd/domain/model/handling/HandlingHistory.java b/full-apps/ddd/src/main/java/org/seedstack/samples/ddd/domain/model/handling/HandlingHistory.java index c6ea2ca..c7c2e9f 100644 --- a/full-apps/ddd/src/main/java/org/seedstack/samples/ddd/domain/model/handling/HandlingHistory.java +++ b/full-apps/ddd/src/main/java/org/seedstack/samples/ddd/domain/model/handling/HandlingHistory.java @@ -16,7 +16,8 @@ import java.util.Comparator; import java.util.HashSet; import java.util.List; -import org.apache.commons.lang.Validate; + +import org.apache.commons.lang3.Validate; import org.seedstack.business.domain.BaseValueObject; /** diff --git a/full-apps/ddd/src/main/java/org/seedstack/samples/ddd/domain/model/location/Location.java b/full-apps/ddd/src/main/java/org/seedstack/samples/ddd/domain/model/location/Location.java index a6bbf21..4df6eb7 100644 --- a/full-apps/ddd/src/main/java/org/seedstack/samples/ddd/domain/model/location/Location.java +++ b/full-apps/ddd/src/main/java/org/seedstack/samples/ddd/domain/model/location/Location.java @@ -10,8 +10,8 @@ import static com.google.common.base.Preconditions.checkNotNull; -import org.mongodb.morphia.annotations.Entity; -import org.mongodb.morphia.annotations.Id; +import dev.morphia.annotations.Entity; +import dev.morphia.annotations.Id; import org.seedstack.business.domain.BaseAggregateRoot; import org.seedstack.business.domain.Identity; diff --git a/full-apps/ddd/src/main/java/org/seedstack/samples/ddd/domain/model/location/UnLocode.java b/full-apps/ddd/src/main/java/org/seedstack/samples/ddd/domain/model/location/UnLocode.java index f7b443c..e235c00 100644 --- a/full-apps/ddd/src/main/java/org/seedstack/samples/ddd/domain/model/location/UnLocode.java +++ b/full-apps/ddd/src/main/java/org/seedstack/samples/ddd/domain/model/location/UnLocode.java @@ -10,7 +10,8 @@ import com.google.common.base.Preconditions; import java.util.regex.Pattern; -import org.mongodb.morphia.annotations.Embedded; + +import dev.morphia.annotations.Embedded; import org.seedstack.business.domain.BaseValueObject; /** diff --git a/full-apps/ddd/src/main/java/org/seedstack/samples/ddd/domain/model/voyage/CarrierMovement.java b/full-apps/ddd/src/main/java/org/seedstack/samples/ddd/domain/model/voyage/CarrierMovement.java index 0081260..a1dcf6c 100644 --- a/full-apps/ddd/src/main/java/org/seedstack/samples/ddd/domain/model/voyage/CarrierMovement.java +++ b/full-apps/ddd/src/main/java/org/seedstack/samples/ddd/domain/model/voyage/CarrierMovement.java @@ -9,8 +9,9 @@ package org.seedstack.samples.ddd.domain.model.voyage; import java.util.Date; -import org.apache.commons.lang.Validate; -import org.mongodb.morphia.annotations.Embedded; + +import dev.morphia.annotations.Embedded; +import org.apache.commons.lang3.Validate; import org.seedstack.business.domain.BaseValueObject; import org.seedstack.samples.ddd.domain.model.location.Location; import org.seedstack.samples.ddd.domain.model.location.UnLocode; diff --git a/full-apps/ddd/src/main/java/org/seedstack/samples/ddd/domain/model/voyage/Schedule.java b/full-apps/ddd/src/main/java/org/seedstack/samples/ddd/domain/model/voyage/Schedule.java index a3f6fc1..babcba3 100644 --- a/full-apps/ddd/src/main/java/org/seedstack/samples/ddd/domain/model/voyage/Schedule.java +++ b/full-apps/ddd/src/main/java/org/seedstack/samples/ddd/domain/model/voyage/Schedule.java @@ -11,8 +11,9 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; -import org.apache.commons.lang.Validate; -import org.mongodb.morphia.annotations.Embedded; + +import dev.morphia.annotations.Embedded; +import org.apache.commons.lang3.Validate; import org.seedstack.business.domain.BaseValueObject; /** diff --git a/full-apps/ddd/src/main/java/org/seedstack/samples/ddd/domain/model/voyage/Voyage.java b/full-apps/ddd/src/main/java/org/seedstack/samples/ddd/domain/model/voyage/Voyage.java index da812f5..6f6e652 100644 --- a/full-apps/ddd/src/main/java/org/seedstack/samples/ddd/domain/model/voyage/Voyage.java +++ b/full-apps/ddd/src/main/java/org/seedstack/samples/ddd/domain/model/voyage/Voyage.java @@ -13,9 +13,10 @@ import java.util.ArrayList; import java.util.Date; import java.util.List; -import org.apache.commons.lang.Validate; -import org.mongodb.morphia.annotations.Entity; -import org.mongodb.morphia.annotations.Id; + +import dev.morphia.annotations.Entity; +import dev.morphia.annotations.Id; +import org.apache.commons.lang3.Validate; import org.seedstack.business.domain.BaseAggregateRoot; import org.seedstack.business.domain.Identity; import org.seedstack.samples.ddd.domain.model.location.UnLocode; diff --git a/full-apps/ddd/src/main/java/org/seedstack/samples/ddd/domain/model/voyage/VoyageNumber.java b/full-apps/ddd/src/main/java/org/seedstack/samples/ddd/domain/model/voyage/VoyageNumber.java index 8aa789b..2626c24 100644 --- a/full-apps/ddd/src/main/java/org/seedstack/samples/ddd/domain/model/voyage/VoyageNumber.java +++ b/full-apps/ddd/src/main/java/org/seedstack/samples/ddd/domain/model/voyage/VoyageNumber.java @@ -10,7 +10,7 @@ import static com.google.common.base.Preconditions.checkNotNull; -import org.mongodb.morphia.annotations.Embedded; +import dev.morphia.annotations.Embedded; import org.seedstack.business.domain.BaseValueObject; /** diff --git a/full-apps/store-webapp/pom.xml b/full-apps/store-webapp/pom.xml index 864d48a..109dbc9 100644 --- a/full-apps/store-webapp/pom.xml +++ b/full-apps/store-webapp/pom.xml @@ -14,8 +14,8 @@ org.seedstack.samples samples - 18.4 - ../.. + 20.11-SNAPSHOT + ../../pom.xml store-webapp-sample @@ -39,9 +39,8 @@ - org.seedstack - web-composite - pom + org.seedstack.seed + seed-rest-jersey2 org.seedstack.seed diff --git a/full-apps/store-webapp/src/main/java/org/seedstack/samples/store/infrastructure/data/DataLifecycleListener.java b/full-apps/store-webapp/src/main/java/org/seedstack/samples/store/infrastructure/data/DataLifecycleListener.java index 5df40ec..987401d 100644 --- a/full-apps/store-webapp/src/main/java/org/seedstack/samples/store/infrastructure/data/DataLifecycleListener.java +++ b/full-apps/store-webapp/src/main/java/org/seedstack/samples/store/infrastructure/data/DataLifecycleListener.java @@ -25,6 +25,9 @@ public class DataLifecycleListener implements LifecycleListener { @JpaUnit("store") public void started() { try (InputStream is = DataLifecycleListener.class.getResourceAsStream("/data.json")) { + if (is == null) { + throw new RuntimeException("Cannot find data.json at classpath root"); + } dataManager.importData(is); } catch (IOException e) { throw new RuntimeException("Unable to import data", e); diff --git a/full-apps/store-webapp/src/main/resources/application.yaml b/full-apps/store-webapp/src/main/resources/application.yaml index 6b25c28..9a577e5 100644 --- a/full-apps/store-webapp/src/main/resources/application.yaml +++ b/full-apps/store-webapp/src/main/resources/application.yaml @@ -40,9 +40,6 @@ security: pattern: /** filters: authcBasic -web: - server: ${env.PORT} - classes: org: seedstack: diff --git a/pom.xml b/pom.xml index 5774ad2..a8b98bf 100644 --- a/pom.xml +++ b/pom.xml @@ -1,7 +1,7 @@