Library Service Guide
What you'll build
You will build a service managing books, authors and subscriptions.
What you’ll need
A Javaâ„¢ Development Kit (JDK) installed. We recommend AdoptOpenJDK version 11 or 8.
Apache Maven version 3.1 or later installed.
An Integrated Developer Environment (IDE)
Popular choices include IntelliJ IDEA, Spring Tools, Visual Studio Code, or Eclipse, and many more.
A FlexiCore based server running locally or on an accessible server.
One-click, no prerequisites installation is available for Linux (AMD64, ARM64) and Windows (AMD64) hereÂ
A Docker image with fully installed FlexiCore and prerequisites is available here
Library Management project is dependent on People Management project , please complete the people management guide.Â
managing entities for this phase:
Create a new Maven Project with the following structure:
update your pom.xml from here
Open up the project in your IDE and create the Book.java
file in the src/main/java/com/flexicore/example/library/model
folder. Now change the contents of the file by adding the extra method and annotations shown in the code below. You can copy and paste the code or just type it.
package com.flexicore.example.library.model;
import com.flexicore.example.person.Person;
import com.flexicore.model.Baseclass;
import com.flexicore.security.SecurityContext;
import javax.persistence.Entity;
import javax.persistence.ManyToOne;
@Entity
public class Book extends Baseclass {
public Book() {
}
public Book(String name, SecurityContext securityContext) {
super(name, securityContext);
}
@ManyToOne(targetEntity = Author.class)
private Author author;
@ManyToOne(targetEntity = Author.class)
public Author getAuthor() {
return author;
}
public <T extends Book> T setAuthor(Author author) {
this.author = author;
return (T) this;
}
}
Book
object inherits from Baseclass
since we want to be able to govern permissions for itOpen up the project in your IDE and create the Author.java
file in the src/main/java/com/flexicore/example/library/model
folder. Now change the contents of the file by adding the extra method and annotations shown in the code below. You can copy and paste the code or just type it.
package com.flexicore.example.library.model;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.flexicore.example.person.Person;
import com.flexicore.security.SecurityContext;
import javax.persistence.Entity;
import javax.persistence.OneToMany;
import java.util.ArrayList;
import java.util.List;
@Entity
public class Author extends Person {
public Author(String name, SecurityContext securityContext) {
super(name, securityContext);
}
public Author() {
}
@OneToMany(targetEntity = Book.class,mappedBy = "author")
@JsonIgnore
private List<Book> books=new ArrayList<>();
@OneToMany(targetEntity = Book.class,mappedBy = "author")
@JsonIgnore
public List<Book> getBooks() {
return books;
}
public <T extends Author> T setBooks(List<Book> books) {
this.books = books;
return (T) this;
}
}
if you haven’t completed the People Management Project it is required to compile library model.
Open up the project in your IDE and create the Subscription.java
file in the src/main/java/com/flexicore/example/library/model
folder. Now change the contents of the file by adding the extra method and annotations shown in the code below. You can copy and paste the code or just type it.
package com.flexicore.example.library.model;
import com.flexicore.example.person.Person;
import com.flexicore.model.Baseclass;
import com.flexicore.security.SecurityContext;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.ManyToOne;
import java.time.OffsetDateTime;
@Entity
public class Subscription extends Baseclass {
@Column(columnDefinition = "timestamp with time zone")
private OffsetDateTime startTime;
@Column(columnDefinition = "timestamp with time zone")
private OffsetDateTime endTime;
public Subscription() {
}
public Subscription(String name, SecurityContext securityContext) {
super(name, securityContext);
}
@ManyToOne(targetEntity = Book.class)
private Book book;
@ManyToOne(targetEntity = Person.class)
private Person person;
@ManyToOne(targetEntity = Book.class)
public Book getBook() {
return book;
}
public <T extends Subscription> T setBook(Book book) {
this.book = book;
return (T) this;
}
@ManyToOne(targetEntity = Person.class)
public Person getPerson() {
return person;
}
public <T extends Subscription> T setPerson(Person person) {
this.person = person;
return (T) this;
}
@Column(columnDefinition = "timestamp with time zone")
public OffsetDateTime getStartTime() {
return startTime;
}
public <T extends Subscription> T setStartTime(OffsetDateTime startTime) {
this.startTime = startTime;
return (T) this;
}
@Column(columnDefinition = "timestamp with time zone")
public OffsetDateTime getEndTime() {
return endTime;
}
public <T extends Subscription> T setEndTime(OffsetDateTime endTime) {
this.endTime = endTime;
return (T) this;
}
}
OffsetDateTime
typed fields are annotated by@Column(columnDefinition = "timestamp with time zone")
to tell our database to save the date information with time zone.copy persistence.xml content from here.
this will allow automatic generation of JPA metamodels required to implement Criteria API based queries.
./mvn install
./cp target/library-model-1.0.0-jar /home/flexicore/entities
create a maven project with the following structure:
update your pom.xml from here
lets define objects that will be consumed by our api:
BookCreate
– this object will contain all required details for creating a Book it will also inherit from BaseclassCreate
object as we would like to extend Baseclass
capabilitiesBookUpdate
– this object will extend BookCreate
object and id of the book to updateBookFilter
– this object will be sent by the client when fetching Books containing filtering options on them.AuthorCreate
– this object will contain all required details for creating an Author it will also inherit from PersonCreate
object as we would like to extend Person
 capabilitiesAuthorUpdate
– this object will extend AuthorCreate
object and id of the Author to updateAuthorFilter
– this object will be sent by the client when fetching Authors containing filtering options on them.SubscriptionCreate
– this object will contain all required details for creating an Subscription it will also inherit from BaseclassCreate
object as we would like to extend Baseclass capabilitiesSubscriptionUpdate
– this object will extend SubscriptionCreate
object and id of the Subscription to updateSubscriptionFilter
– this object will be sent by the client when fetching Subscriptions containing filtering options on them.Â
package com.flexicore.examples.request;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.flexicore.example.library.model.Author;
public class BookCreate {
private String name;
private String description;
private String authorId;
@JsonIgnore
private Author author;
public String getName() {
return name;
}
public <T extends BookCreate> T setName(String name) {
this.name = name;
return (T) this;
}
public String getDescription() {
return description;
}
public <T extends BookCreate> T setDescription(String description) {
this.description = description;
return (T) this;
}
public String getAuthorId() {
return authorId;
}
public <T extends BookCreate> T setAuthorId(String authorId) {
this.authorId = authorId;
return (T) this;
}
@JsonIgnore
public Author getAuthor() {
return author;
}
public <T extends BookCreate> T setAuthor(Author author) {
this.author = author;
return (T) this;
}
}
package com.flexicore.examples.request;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.flexicore.example.library.model.Book;
public class BookUpdate extends BookCreate {
private String id;
@JsonIgnore
private Book book;
public String getId() {
return id;
}
public <T extends BookUpdate> T setId(String id) {
this.id = id;
return (T) this;
}
@JsonIgnore
public Book getBook() {
return book;
}
public <T extends BookUpdate> T setBook(Book book) {
this.book = book;
return (T) this;
}
}
package com.flexicore.examples.request;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.flexicore.example.library.model.Author;
import com.flexicore.model.FilteringInformationHolder;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class BookFilter extends FilteringInformationHolder {
private Set<String> authorIds = new HashSet<>();
@JsonIgnore
private List<Author> authors;
public Set<String> getAuthorIds() {
return authorIds;
}
public <T extends BookFilter> T setAuthorIds(Set<String> authorIds) {
this.authorIds = authorIds;
return (T) this;
}
@JsonIgnore
public List<Author> getAuthors() {
return authors;
}
public <T extends BookFilter> T setAuthors(List<Author> authors) {
this.authors = authors;
return (T) this;
}
}
package com.flexicore.examples.request;
public class AuthorCreate extends PersonCreate {
}
package com.flexicore.examples.request;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.flexicore.example.library.model.Author;
public class AuthorUpdate extends AuthorCreate {
private String id;
@JsonIgnore
private Author author;
public String getId() {
return id;
}
public <T extends AuthorUpdate> T setId(String id) {
this.id = id;
return (T) this;
}
@JsonIgnore
public Author getAuthor() {
return author;
}
public <T extends AuthorUpdate> T setAuthor(Author author) {
this.author = author;
return (T) this;
}
}
package com.flexicore.examples.request;
public class AuthorFilter extends PersonFilter {
}
package com.flexicore.examples.request;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.flexicore.example.library.model.Book;
import com.flexicore.example.person.Person;
import java.time.OffsetDateTime;
public class SubscriptionCreate {
private String description;
private OffsetDateTime startTime;
private OffsetDateTime endTime;
private String bookId;
@JsonIgnore
private Book book;
private String personId;
@JsonIgnore
private Person person;
public String getDescription() {
return description;
}
public <T extends SubscriptionCreate> T setDescription(String description) {
this.description = description;
return (T) this;
}
public OffsetDateTime getStartTime() {
return startTime;
}
public <T extends SubscriptionCreate> T setStartTime(OffsetDateTime startTime) {
this.startTime = startTime;
return (T) this;
}
public OffsetDateTime getEndTime() {
return endTime;
}
public <T extends SubscriptionCreate> T setEndTime(OffsetDateTime endTime) {
this.endTime = endTime;
return (T) this;
}
public String getBookId() {
return bookId;
}
public <T extends SubscriptionCreate> T setBookId(String bookId) {
this.bookId = bookId;
return (T) this;
}
@JsonIgnore
public Book getBook() {
return book;
}
public <T extends SubscriptionCreate> T setBook(Book book) {
this.book = book;
return (T) this;
}
public String getPersonId() {
return personId;
}
public <T extends SubscriptionCreate> T setPersonId(String personId) {
this.personId = personId;
return (T) this;
}
@JsonIgnore
public Person getPerson() {
return person;
}
public <T extends SubscriptionCreate> T setPerson(Person person) {
this.person = person;
return (T) this;
}
}
package com.flexicore.examples.request;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.flexicore.example.library.model.Book;
import com.flexicore.example.person.Person;
import com.flexicore.model.FilteringInformationHolder;
import java.time.OffsetDateTime;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class SubscriptionFilter extends FilteringInformationHolder {
private Set<String> bookIds = new HashSet<>();
@JsonIgnore
private List<Book> books;
private Set<String> personIds = new HashSet<>();
@JsonIgnore
private List<Person> persons;
public Set<String> getBookIds() {
return bookIds;
}
public <T extends SubscriptionFilter> T setBookIds(Set<String> bookIds) {
this.bookIds = bookIds;
return (T) this;
}
@JsonIgnore
public List<Book> getBooks() {
return books;
}
public <T extends SubscriptionFilter> T setBooks(List<Book> books) {
this.books = books;
return (T) this;
}
public Set<String> getPersonIds() {
return personIds;
}
public <T extends SubscriptionFilter> T setPersonIds(Set<String> personIds) {
this.personIds = personIds;
return (T) this;
}
@JsonIgnore
public List<Person> getPersons() {
return persons;
}
public <T extends SubscriptionFilter> T setPersons(List<Person> persons) {
this.persons = persons;
return (T) this;
}
}
package com.flexicore.examples.request;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.flexicore.example.library.model.Subscription;
public class SubscriptionUpdate extends SubscriptionCreate {
private String id;
@JsonIgnore
private Subscription subscription;
public String getId() {
return id;
}
public <T extends SubscriptionUpdate> T setId(String id) {
this.id = id;
return (T) this;
}
@JsonIgnore
public Subscription getSubscription() {
return subscription;
}
public <T extends SubscriptionUpdate> T setSubscription(
Subscription subscription) {
this.subscription = subscription;
return (T) this;
}
}
lets define the repositories that will be used to fetch and save books, authors and subscriptions from the database.
package com.flexicore.examples.data;
import com.flexicore.annotations.plugins.PluginInfo;
import com.flexicore.example.library.model.Author;
import com.flexicore.example.library.model.Author_;
import com.flexicore.example.library.model.Book;
import com.flexicore.example.library.model.Book_;
import com.flexicore.examples.request.BookFilter;
import com.flexicore.interfaces.AbstractRepositoryPlugin;
import com.flexicore.model.QueryInformationHolder;
import com.flexicore.security.SecurityContext;
import javax.persistence.criteria.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import org.pf4j.Extension;
import org.springframework.stereotype.Component;
@PluginInfo(version = 1)
@Extension
@Component
public class BookRepository extends AbstractRepositoryPlugin {
public List<Book> listAllBooks(BookFilter filtering,
SecurityContext securityContext) {
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Book> q = cb.createQuery(Book.class);
Root<Book> r = q.from(Book.class);
List<Predicate> preds = new ArrayList<>();
addBookPredicate(filtering, cb, r, preds);
QueryInformationHolder<Book> queryInformationHolder = new QueryInformationHolder<>(
filtering, Book.class, securityContext);
return getAllFiltered(queryInformationHolder, preds, cb, q, r);
}
private void addBookPredicate(BookFilter filtering, CriteriaBuilder cb,
Root<Book> r, List<Predicate> preds) {
if (filtering.getAuthors() != null && !filtering.getAuthors().isEmpty()) {
Set<String> ids = filtering.getAuthors().parallelStream()
.map(f -> f.getId()).collect(Collectors.toSet());
Join<Book, Author> join = r.join(Book_.author);
preds.add(join.get(Author_.id).in(ids));
}
}
public Long countAllBooks(BookFilter filtering,
SecurityContext securityContext) {
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Long> q = cb.createQuery(Long.class);
Root<Book> r = q.from(Book.class);
List<Predicate> preds = new ArrayList<>();
addBookPredicate(filtering, cb, r, preds);
QueryInformationHolder<Book> queryInformationHolder = new QueryInformationHolder<>(
filtering, Book.class, securityContext);
return countAllFiltered(queryInformationHolder, preds, cb, q, r);
}
}
package com.flexicore.examples.data;
import com.flexicore.annotations.plugins.PluginInfo;
import com.flexicore.example.library.model.Author;
import com.flexicore.examples.request.AuthorFilter;
import com.flexicore.interfaces.AbstractRepositoryPlugin;
import com.flexicore.model.QueryInformationHolder;
import com.flexicore.security.SecurityContext;
import org.pf4j.Extension;
import org.springframework.stereotype.Component;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import java.util.ArrayList;
import java.util.List;
@PluginInfo(version = 1)
@Extension
@Component
public class AuthorRepository extends AbstractRepositoryPlugin {
public List<Author> listAllAuthors(AuthorFilter filtering,
SecurityContext securityContext) {
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Author> q = cb.createQuery(Author.class);
Root<Author> r = q.from(Author.class);
List<Predicate> preds = new ArrayList<>();
addAuthorPredicate(filtering, cb, r, preds);
QueryInformationHolder<Author> queryInformationHolder = new QueryInformationHolder<>(
filtering, Author.class, securityContext);
return getAllFiltered(queryInformationHolder, preds, cb, q, r);
}
private void addAuthorPredicate(AuthorFilter filtering, CriteriaBuilder cb,
Root<Author> r, List<Predicate> preds) {
PersonRepository.addPersonPredicate(filtering, cb, r, preds);
}
public Long countAllAuthors(AuthorFilter filtering,
SecurityContext securityContext) {
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Long> q = cb.createQuery(Long.class);
Root<Author> r = q.from(Author.class);
List<Predicate> preds = new ArrayList<>();
addAuthorPredicate(filtering, cb, r, preds);
QueryInformationHolder<Author> queryInformationHolder = new QueryInformationHolder<>(
filtering, Author.class, securityContext);
return countAllFiltered(queryInformationHolder, preds, cb, q, r);
}
}
package com.flexicore.examples.data;
import com.flexicore.annotations.plugins.PluginInfo;
import com.flexicore.example.library.model.Book;
import com.flexicore.example.library.model.Book_;
import com.flexicore.example.library.model.Subscription;
import com.flexicore.example.library.model.Subscription_;
import com.flexicore.example.person.Person;
import com.flexicore.example.person.Person_;
import com.flexicore.examples.request.SubscriptionFilter;
import com.flexicore.interfaces.AbstractRepositoryPlugin;
import com.flexicore.model.QueryInformationHolder;
import com.flexicore.security.SecurityContext;
import javax.persistence.criteria.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import org.pf4j.Extension;
import org.springframework.stereotype.Component;
@PluginInfo(version = 1)
@Extension
@Component
public class SubscriptionRepository extends AbstractRepositoryPlugin {
public List<Subscription> listAllSubscriptions(
SubscriptionFilter filtering, SecurityContext securityContext) {
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Subscription> q = cb.createQuery(Subscription.class);
Root<Subscription> r = q.from(Subscription.class);
List<Predicate> preds = new ArrayList<>();
addSubscriptionPredicate(filtering, cb, r, preds);
QueryInformationHolder<Subscription> queryInformationHolder = new QueryInformationHolder<>(
filtering, Subscription.class, securityContext);
return getAllFiltered(queryInformationHolder, preds, cb, q, r);
}
private void addSubscriptionPredicate(SubscriptionFilter filtering,
CriteriaBuilder cb, Root<Subscription> r, List<Predicate> preds) {
if (filtering.getBooks() != null && !filtering.getBooks().isEmpty()) {
Set<String> ids = filtering.getBooks().parallelStream()
.map(f -> f.getId()).collect(Collectors.toSet());
Join<Subscription, Book> join = r.join(Subscription_.book);
preds.add(join.get(Book_.id).in(ids));
}
if (filtering.getPersons() != null && !filtering.getPersons().isEmpty()) {
Set<String> ids = filtering.getPersons().parallelStream()
.map(f -> f.getId()).collect(Collectors.toSet());
Join<Subscription, Person> join = r.join(Subscription_.person);
preds.add(join.get(Person_.id).in(ids));
}
}
public Long countAllSubscriptions(SubscriptionFilter filtering,
SecurityContext securityContext) {
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Long> q = cb.createQuery(Long.class);
Root<Subscription> r = q.from(Subscription.class);
List<Predicate> preds = new ArrayList<>();
addSubscriptionPredicate(filtering, cb, r, preds);
QueryInformationHolder<Subscription> queryInformationHolder = new QueryInformationHolder<>(
filtering, Subscription.class, securityContext);
return countAllFiltered(queryInformationHolder, preds, cb, q, r);
}
}
@Extension
annotation to allow FlexiCore to load it as a plugin @PluginInfo
annotation to allow future versioning support@Component
annotation to let spring know it is a bean.AbstractRepositoryPlugin
which provides easy method for access control and out of the box methods for persisting objectsBook/Author/Subscription
 both are calling the relevant addBookPredicates/addAuthorPredicates/addSubscriptionPredicates
which adds the required predicates , all access control predicates are automatically added when countAllFiltered
and getAllFiltered
are calledaddBookPredicates/addAuthorPredicates/addSubscriptionPredicates
uses JPA Criteria Api to filter data based on BookFilter/AuthorFilter/SubscriptionFilter
objects we have created in previous phase.lets define the services that will be used by other plugins and REST api (or any other API implementations for that matter)
package com.flexicore.examples.service;
import com.flexicore.annotations.plugins.PluginInfo;
import com.flexicore.data.jsoncontainers.PaginationResponse;
import com.flexicore.example.library.model.Author;
import com.flexicore.example.library.model.Book;
import com.flexicore.examples.data.BookRepository;
import com.flexicore.examples.request.BookCreate;
import com.flexicore.examples.request.BookFilter;
import com.flexicore.examples.request.BookUpdate;
import com.flexicore.interfaces.ServicePlugin;
import com.flexicore.model.Baseclass;
import com.flexicore.security.SecurityContext;
import javax.ws.rs.BadRequestException;
import java.util.*;
import java.util.logging.Logger;
import java.util.stream.Collectors;
import org.pf4j.Extension;
import org.springframework.stereotype.Component;
import org.springframework.beans.factory.annotation.Autowired;
@PluginInfo(version = 1)
@Extension
@Component
public class BookService implements ServicePlugin {
@PluginInfo(version = 1)
@Autowired
private BookRepository repository;
public Book createBook(BookCreate bookCreate,
SecurityContext securityContext) {
Book book = createBookNoMerge(bookCreate, securityContext);
repository.merge(book);
return book;
}
public Book createBookNoMerge(BookCreate bookCreate,
SecurityContext securityContext) {
Book book = new Book(bookCreate.getName(),securityContext);
updateBookNoMerge(book, bookCreate);
return book;
}
public boolean updateBookNoMerge(Book book, BookCreate bookCreate) {
boolean update = false;
if (bookCreate.getName() != null
&& !bookCreate.getName().equals(book.getName())) {
book.setName(bookCreate.getName());
update = true;
}
if (bookCreate.getDescription() != null
&& !bookCreate.getDescription().equals(book.getDescription())) {
book.setDescription(bookCreate.getDescription());
update = true;
}
if (bookCreate.getAuthor() != null
&& (book.getAuthor() == null || !bookCreate.getAuthor().getId()
.equals(book.getAuthor().getId()))) {
book.setAuthor(bookCreate.getAuthor());
update = true;
}
return update;
}
public Book updateBook(BookUpdate bookUpdate,
SecurityContext securityContext) {
Book book = bookUpdate.getBook();
if (updateBookNoMerge(book, bookUpdate)) {
repository.merge(book);
}
return book;
}
public <T extends Baseclass> T getByIdOrNull(String id, Class<T> c,
List<String> batchString, SecurityContext securityContext) {
return repository.getByIdOrNull(id, c, batchString, securityContext);
}
public PaginationResponse<Book> getAllBooks(BookFilter bookFilter,
SecurityContext securityContext) {
List<Book> list = listAllBooks(bookFilter, securityContext);
long count = repository.countAllBooks(bookFilter, securityContext);
return new PaginationResponse<>(list, bookFilter, count);
}
public List<Book> listAllBooks(BookFilter bookFilter,
SecurityContext securityContext) {
return repository.listAllBooks(bookFilter, securityContext);
}
public void validate(BookFilter bookFilter, SecurityContext securityContext) {
Set<String> authorIds = bookFilter.getAuthorIds();
Map<String, Author> authorMap = authorIds.isEmpty()
? new HashMap<>()
: repository
.listByIds(Author.class, authorIds, securityContext)
.parallelStream()
.collect(Collectors.toMap(f -> f.getId(), f -> f));
authorIds.removeAll(authorMap.keySet());
if (!authorIds.isEmpty()) {
throw new BadRequestException("No Authors with ids " + authorIds);
}
bookFilter.setAuthors(new ArrayList<>(authorMap.values()));
}
public void validate(BookCreate bookCreate, SecurityContext securityContext) {
String authorId = bookCreate.getAuthorId();
Author author = authorId != null ? getByIdOrNull(authorId,
Author.class, null, securityContext) : null;
if (author == null) {
throw new BadRequestException("No Author with id " + authorId);
}
bookCreate.setAuthor(author);
}
}
package com.flexicore.examples.service;
import com.flexicore.annotations.plugins.PluginInfo;
import com.flexicore.data.jsoncontainers.PaginationResponse;
import com.flexicore.example.library.model.Author;
import com.flexicore.examples.data.AuthorRepository;
import com.flexicore.examples.request.AuthorCreate;
import com.flexicore.examples.request.AuthorFilter;
import com.flexicore.examples.request.AuthorUpdate;
import com.flexicore.interfaces.ServicePlugin;
import com.flexicore.model.Baseclass;
import com.flexicore.security.SecurityContext;
import org.pf4j.Extension;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.logging.Logger;
@PluginInfo(version = 1)
@Component
@Extension
@Primary
public class AuthorService implements ServicePlugin {
@PluginInfo(version = 1)
@Autowired
private AuthorRepository repository;
@PluginInfo(version = 1)
@Autowired
private PersonService personService;
public Author createAuthor(AuthorCreate authorCreate,
SecurityContext securityContext) {
Author author = createAuthorNoMerge(authorCreate, securityContext);
repository.merge(author);
return author;
}
public Author createAuthorNoMerge(AuthorCreate authorCreate,
SecurityContext securityContext) {
Author author = new Author(authorCreate.getFirstName(),securityContext);
updateAuthorNoMerge(author, authorCreate);
return author;
}
public boolean updateAuthorNoMerge(Author author, AuthorCreate authorCreate) {
boolean update =personService.updatePersonNoMerge(author,authorCreate);
return update;
}
public Author updateAuthor(AuthorUpdate authorUpdate,
SecurityContext securityContext) {
Author author = authorUpdate.getAuthor();
if (updateAuthorNoMerge(author, authorUpdate)) {
repository.merge(author);
}
return author;
}
public <T extends Baseclass> T getByIdOrNull(String id, Class<T> c,
List<String> batchString, SecurityContext securityContext) {
return repository.getByIdOrNull(id, c, batchString, securityContext);
}
public PaginationResponse<Author> getAllAuthors(AuthorFilter authorFilter,
SecurityContext securityContext) {
List<Author> list = listAllAuthors(authorFilter, securityContext);
long count = repository.countAllAuthors(authorFilter, securityContext);
return new PaginationResponse<>(list, authorFilter, count);
}
public List<Author> listAllAuthors(AuthorFilter authorFilter,
SecurityContext securityContext) {
return repository.listAllAuthors(authorFilter, securityContext);
}
public void validate(AuthorFilter authorFilter,
SecurityContext securityContext) {
}
public void validate(AuthorCreate authorCreate,
SecurityContext securityContext) {
}
}
package com.flexicore.examples.service;
import com.flexicore.annotations.plugins.PluginInfo;
import com.flexicore.data.jsoncontainers.PaginationResponse;
import com.flexicore.example.library.model.Author;
import com.flexicore.example.library.model.Book;
import com.flexicore.example.library.model.Subscription;
import com.flexicore.example.person.Person;
import com.flexicore.examples.data.SubscriptionRepository;
import com.flexicore.examples.request.SubscriptionCreate;
import com.flexicore.examples.request.SubscriptionFilter;
import com.flexicore.examples.request.SubscriptionUpdate;
import com.flexicore.interfaces.ServicePlugin;
import com.flexicore.model.Baseclass;
import com.flexicore.security.SecurityContext;
import javax.ws.rs.BadRequestException;
import java.util.*;
import java.util.logging.Logger;
import java.util.stream.Collectors;
import org.pf4j.Extension;
import org.springframework.stereotype.Component;
import org.springframework.beans.factory.annotation.Autowired;
@PluginInfo(version = 1)
@Extension
@Component
public class SubscriptionService implements ServicePlugin {
@PluginInfo(version = 1)
@Autowired
private SubscriptionRepository repository;
public Subscription createSubscription(
SubscriptionCreate subscriptionCreate,
SecurityContext securityContext) {
Subscription subscription = createSubscriptionNoMerge(
subscriptionCreate, securityContext);
repository.merge(subscription);
return subscription;
}
public Subscription createSubscriptionNoMerge(
SubscriptionCreate subscriptionCreate,
SecurityContext securityContext) {
Subscription subscription = new Subscription("subscription",securityContext);
updateSubscriptionNoMerge(subscription, subscriptionCreate);
return subscription;
}
public boolean updateSubscriptionNoMerge(Subscription subscription,
SubscriptionCreate subscriptionCreate) {
boolean update = false;
if (subscriptionCreate.getDescription() != null
&& !subscriptionCreate.getDescription().equals(
subscription.getDescription())) {
subscription.setDescription(subscriptionCreate.getDescription());
update = true;
}
if (subscriptionCreate.getEndTime() != null
&& !subscriptionCreate.getEndTime().equals(
subscription.getEndTime())) {
subscription.setEndTime(subscriptionCreate.getEndTime());
update = true;
}
if (subscriptionCreate.getStartTime() != null
&& !subscriptionCreate.getStartTime().equals(
subscription.getStartTime())) {
subscription.setStartTime(subscriptionCreate.getStartTime());
update = true;
}
if (subscriptionCreate.getPerson() != null
&& (subscription.getPerson() == null || !subscriptionCreate
.getPerson().getId()
.equals(subscription.getPerson().getId()))) {
subscription.setPerson(subscriptionCreate.getPerson());
update = true;
}
if (subscriptionCreate.getBook() != null
&& (subscription.getBook() == null || !subscriptionCreate
.getBook().getId()
.equals(subscription.getBook().getId()))) {
subscription.setBook(subscriptionCreate.getBook());
update = true;
}
return update;
}
public Subscription updateSubscription(
SubscriptionUpdate subscriptionUpdate,
SecurityContext securityContext) {
Subscription subscription = subscriptionUpdate.getSubscription();
if (updateSubscriptionNoMerge(subscription, subscriptionUpdate)) {
repository.merge(subscription);
}
return subscription;
}
public <T extends Baseclass> T getByIdOrNull(String id, Class<T> c,
List<String> batchString, SecurityContext securityContext) {
return repository.getByIdOrNull(id, c, batchString, securityContext);
}
public PaginationResponse<Subscription> getAllSubscriptions(
SubscriptionFilter subscriptionFilter,
SecurityContext securityContext) {
List<Subscription> list = listAllSubscriptions(subscriptionFilter,
securityContext);
long count = repository.countAllSubscriptions(subscriptionFilter,
securityContext);
return new PaginationResponse<>(list, subscriptionFilter, count);
}
public List<Subscription> listAllSubscriptions(
SubscriptionFilter subscriptionFilter,
SecurityContext securityContext) {
return repository.listAllSubscriptions(subscriptionFilter,
securityContext);
}
public void validate(SubscriptionFilter subscriptionFilter,
SecurityContext securityContext) {
Set<String> bookIds = subscriptionFilter.getBookIds();
Map<String, Book> bookMap = bookIds.isEmpty()
? new HashMap<>()
: repository.listByIds(Book.class, bookIds, securityContext)
.parallelStream()
.collect(Collectors.toMap(f -> f.getId(), f -> f));
bookIds.removeAll(bookMap.keySet());
if (!bookIds.isEmpty()) {
throw new BadRequestException("No Books with ids " + bookIds);
}
subscriptionFilter.setBooks(new ArrayList<>(bookMap.values()));
Set<String> personIds = subscriptionFilter.getPersonIds();
Map<String, Person> personMap = personIds.isEmpty()
? new HashMap<>()
: repository
.listByIds(Person.class, personIds, securityContext)
.parallelStream()
.collect(Collectors.toMap(f -> f.getId(), f -> f));
personIds.removeAll(personMap.keySet());
if (!personIds.isEmpty()) {
throw new BadRequestException("No Person with ids " + personIds);
}
subscriptionFilter.setPersons(new ArrayList<>(personMap.values()));
}
public void validate(SubscriptionCreate subscriptionCreate,
SecurityContext securityContext) {
String bookId = subscriptionCreate.getBookId();
Book book = bookId != null ? getByIdOrNull(bookId, Book.class, null,
securityContext) : null;
if (bookId!=null&&book == null) {
throw new BadRequestException("No Book with id " + bookId);
}
subscriptionCreate.setBook(book);
String personId = subscriptionCreate.getPersonId();
Person person = personId != null ? getByIdOrNull(personId,
Person.class, null, securityContext) : null;
if (personId!=null&&person == null) {
throw new BadRequestException("No Person with id " + personId);
}
subscriptionCreate.setPerson(person);
}
}
lets define the REST services that will expose REST API that our clients will use:
package com.flexicore.examples.rest;
import com.flexicore.annotations.OperationsInside;
import com.flexicore.annotations.ProtectedREST;
import com.flexicore.annotations.plugins.PluginInfo;
import com.flexicore.data.jsoncontainers.PaginationResponse;
import com.flexicore.example.library.model.Book;
import com.flexicore.examples.request.BookCreate;
import com.flexicore.examples.request.BookFilter;
import com.flexicore.examples.request.BookUpdate;
import com.flexicore.examples.service.BookService;
import com.flexicore.interfaces.RestServicePlugin;
import com.flexicore.security.SecurityContext;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import javax.interceptor.Interceptors;
import javax.ws.rs.*;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import org.pf4j.Extension;
import org.springframework.stereotype.Component;
import org.springframework.beans.factory.annotation.Autowired;
/**
* Created by Asaf on 04/06/2017.
*/
@PluginInfo(version = 1)
@OperationsInside
@ProtectedREST
@Path("plugins/Book")
@Tag(name = "Book")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
@Extension
@Component
public class BookRESTService implements RestServicePlugin {
@PluginInfo(version = 1)
@Autowired
private BookService service;
@POST
@Path("createBook")
@Operation(summary = "createBook", description = "Creates Book")
public Book createBook(
@HeaderParam("authenticationKey") String authenticationKey,
BookCreate bookCreate, @Context SecurityContext securityContext) {
service.validate(bookCreate, securityContext);
return service.createBook(bookCreate, securityContext);
}
@PUT
@Operation(summary = "updateBook", description = "Updates Book")
@Path("updateBook")
public Book updateBook(
@HeaderParam("authenticationKey") String authenticationKey,
BookUpdate bookUpdate, @Context SecurityContext securityContext) {
String bookId = bookUpdate.getId();
Book book = service.getByIdOrNull(bookId, Book.class, null,
securityContext);
if (book == null) {
throw new BadRequestException("No Book with id " + bookId);
}
bookUpdate.setBook(book);
service.validate(bookUpdate, securityContext);
return service.updateBook(bookUpdate, securityContext);
}
@POST
@Operation(summary = "getAllBooks", description = "Gets All Books Filtered")
@Path("getAllBooks")
public PaginationResponse<Book> getAllBooks(
@HeaderParam("authenticationKey") String authenticationKey,
BookFilter bookFilter, @Context SecurityContext securityContext) {
service.validate(bookFilter, securityContext);
return service.getAllBooks(bookFilter, securityContext);
}
}
package com.flexicore.examples.rest;
import com.flexicore.annotations.OperationsInside;
import com.flexicore.annotations.ProtectedREST;
import com.flexicore.annotations.plugins.PluginInfo;
import com.flexicore.data.jsoncontainers.PaginationResponse;
import com.flexicore.example.library.model.Author;
import com.flexicore.examples.request.AuthorCreate;
import com.flexicore.examples.request.AuthorFilter;
import com.flexicore.examples.request.AuthorUpdate;
import com.flexicore.examples.service.AuthorService;
import com.flexicore.interfaces.RestServicePlugin;
import com.flexicore.security.SecurityContext;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import javax.ws.rs.*;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import org.pf4j.Extension;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;
import org.springframework.beans.factory.annotation.Autowired;
/**
* Created by Asaf on 04/06/2017.
*/
@PluginInfo(version = 1)
@OperationsInside
@ProtectedREST
@Path("plugins/Author")
@Tag(name = "Author")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
@Component
@Extension
@Primary
public class AuthorRESTService implements RestServicePlugin {
@PluginInfo(version = 1)
@Autowired
private AuthorService authorService;
@POST
@Path("createAuthor")
@Operation(summary = "createAuthor", description = "Creates Author")
public Author createAuthor(
@HeaderParam("authenticationKey") String authenticationKey,
AuthorCreate authorCreate, @Context SecurityContext securityContext) {
authorService.validate(authorCreate, securityContext);
return authorService.createAuthor(authorCreate, securityContext);
}
@PUT
@Operation(summary = "updateAuthor", description = "Updates Author")
@Path("updateAuthor")
public void updateAuthor(
@HeaderParam("authenticationKey") String authenticationKey,
AuthorUpdate authorUpdate, @Context SecurityContext securityContext) {
String authorId = authorUpdate.getId();
Author author = authorService.getByIdOrNull(authorId, Author.class, null,
securityContext);
if (author == null) {
throw new BadRequestException("No Author with id " + authorId);
}
authorUpdate.setAuthor(author);
authorService.validate(authorUpdate, securityContext);
authorService.updateAuthor(authorUpdate, securityContext);
}
@POST
@Operation(summary = "getAllAuthors", description = "Gets All Authors Filtered")
@Path("getAllAuthors")
public PaginationResponse<Author> getAllAuthors(
@HeaderParam("authenticationKey") String authenticationKey,
AuthorFilter authorFilter, @Context SecurityContext securityContext) {
authorService.validate(authorFilter, securityContext);
return authorService.getAllAuthors(authorFilter, securityContext);
}
}
package com.flexicore.examples.service;
import com.flexicore.annotations.plugins.PluginInfo;
import com.flexicore.data.jsoncontainers.PaginationResponse;
import com.flexicore.example.library.model.Author;
import com.flexicore.example.library.model.Book;
import com.flexicore.example.library.model.Subscription;
import com.flexicore.example.person.Person;
import com.flexicore.examples.data.SubscriptionRepository;
import com.flexicore.examples.request.SubscriptionCreate;
import com.flexicore.examples.request.SubscriptionFilter;
import com.flexicore.examples.request.SubscriptionUpdate;
import com.flexicore.interfaces.ServicePlugin;
import com.flexicore.model.Baseclass;
import com.flexicore.security.SecurityContext;
import javax.ws.rs.BadRequestException;
import java.util.*;
import java.util.logging.Logger;
import java.util.stream.Collectors;
import org.pf4j.Extension;
import org.springframework.stereotype.Component;
import org.springframework.beans.factory.annotation.Autowired;
@PluginInfo(version = 1)
@Extension
@Component
public class SubscriptionService implements ServicePlugin {
@PluginInfo(version = 1)
@Autowired
private SubscriptionRepository repository;
public Subscription createSubscription(
SubscriptionCreate subscriptionCreate,
SecurityContext securityContext) {
Subscription subscription = createSubscriptionNoMerge(
subscriptionCreate, securityContext);
repository.merge(subscription);
return subscription;
}
public Subscription createSubscriptionNoMerge(
SubscriptionCreate subscriptionCreate,
SecurityContext securityContext) {
Subscription subscription = new Subscription("subscription",securityContext);
updateSubscriptionNoMerge(subscription, subscriptionCreate);
return subscription;
}
public boolean updateSubscriptionNoMerge(Subscription subscription,
SubscriptionCreate subscriptionCreate) {
boolean update = false;
if (subscriptionCreate.getDescription() != null
&& !subscriptionCreate.getDescription().equals(
subscription.getDescription())) {
subscription.setDescription(subscriptionCreate.getDescription());
update = true;
}
if (subscriptionCreate.getEndTime() != null
&& !subscriptionCreate.getEndTime().equals(
subscription.getEndTime())) {
subscription.setEndTime(subscriptionCreate.getEndTime());
update = true;
}
if (subscriptionCreate.getStartTime() != null
&& !subscriptionCreate.getStartTime().equals(
subscription.getStartTime())) {
subscription.setStartTime(subscriptionCreate.getStartTime());
update = true;
}
if (subscriptionCreate.getPerson() != null
&& (subscription.getPerson() == null || !subscriptionCreate
.getPerson().getId()
.equals(subscription.getPerson().getId()))) {
subscription.setPerson(subscriptionCreate.getPerson());
update = true;
}
if (subscriptionCreate.getBook() != null
&& (subscription.getBook() == null || !subscriptionCreate
.getBook().getId()
.equals(subscription.getBook().getId()))) {
subscription.setBook(subscriptionCreate.getBook());
update = true;
}
return update;
}
public Subscription updateSubscription(
SubscriptionUpdate subscriptionUpdate,
SecurityContext securityContext) {
Subscription subscription = subscriptionUpdate.getSubscription();
if (updateSubscriptionNoMerge(subscription, subscriptionUpdate)) {
repository.merge(subscription);
}
return subscription;
}
public <T extends Baseclass> T getByIdOrNull(String id, Class<T> c,
List<String> batchString, SecurityContext securityContext) {
return repository.getByIdOrNull(id, c, batchString, securityContext);
}
public PaginationResponse<Subscription> getAllSubscriptions(
SubscriptionFilter subscriptionFilter,
SecurityContext securityContext) {
List<Subscription> list = listAllSubscriptions(subscriptionFilter,
securityContext);
long count = repository.countAllSubscriptions(subscriptionFilter,
securityContext);
return new PaginationResponse<>(list, subscriptionFilter, count);
}
public List<Subscription> listAllSubscriptions(
SubscriptionFilter subscriptionFilter,
SecurityContext securityContext) {
return repository.listAllSubscriptions(subscriptionFilter,
securityContext);
}
public void validate(SubscriptionFilter subscriptionFilter,
SecurityContext securityContext) {
Set<String> bookIds = subscriptionFilter.getBookIds();
Map<String, Book> bookMap = bookIds.isEmpty()
? new HashMap<>()
: repository.listByIds(Book.class, bookIds, securityContext)
.parallelStream()
.collect(Collectors.toMap(f -> f.getId(), f -> f));
bookIds.removeAll(bookMap.keySet());
if (!bookIds.isEmpty()) {
throw new BadRequestException("No Books with ids " + bookIds);
}
subscriptionFilter.setBooks(new ArrayList<>(bookMap.values()));
Set<String> personIds = subscriptionFilter.getPersonIds();
Map<String, Person> personMap = personIds.isEmpty()
? new HashMap<>()
: repository
.listByIds(Person.class, personIds, securityContext)
.parallelStream()
.collect(Collectors.toMap(f -> f.getId(), f -> f));
personIds.removeAll(personMap.keySet());
if (!personIds.isEmpty()) {
throw new BadRequestException("No Person with ids " + personIds);
}
subscriptionFilter.setPersons(new ArrayList<>(personMap.values()));
}
public void validate(SubscriptionCreate subscriptionCreate,
SecurityContext securityContext) {
String bookId = subscriptionCreate.getBookId();
Book book = bookId != null ? getByIdOrNull(bookId, Book.class, null,
securityContext) : null;
if (bookId!=null&&book == null) {
throw new BadRequestException("No Book with id " + bookId);
}
subscriptionCreate.setBook(book);
String personId = subscriptionCreate.getPersonId();
Person person = personId != null ? getByIdOrNull(personId,
Person.class, null, securityContext) : null;
if (personId!=null&&person == null) {
throw new BadRequestException("No Person with id " + personId);
}
subscriptionCreate.setPerson(person);
}
}
Success Stories