People Service Guide

What you'll build

You will build a simple service that allows managing people.

What you’ll need

Development requirements

A Java™ Development Kit (JDK) installed. We recommend AdoptOpenJDK version 11 or 8.

Apache Maven version 3.1 or later installed.

Optional

An Integrated Developer Environment (IDE)

Popular choices include IntelliJ IDEA, Spring Tools, Visual Studio Code, or Eclipse, and many more.

Testing and running requirements

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

Step 1: Person Model

Create a new Maven Project with the following structure:

People Model Project Structure

update your pom.xml from here

Step 1.a: Create Person Entity

Open up the project in your IDE and create the Person.java file in the src/main/java/com/flexicore/examples/person/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.person;

import com.flexicore.model.Baseclass;
import com.flexicore.security.SecurityContext;

import javax.persistence.Entity;

@Entity
public class Person extends Baseclass {
    public Person(String name, SecurityContext securityContext) {
        super(name, securityContext);
    }

    public Person() {
    }

    private String firstName;
    private String lastName;

    public String getFirstName() {
        return firstName;
    }

    public <T extends Person> T setFirstName(String firstName) {
        this.firstName = firstName;
        return (T) this;
    }

    public String getLastName() {
        return lastName;
    }

    public <T extends Person> T setLastName(String lastName) {
        this.lastName = lastName;
        return (T) this;
    }
} 
  1. Our Person object inherits from Baseclass since we want to be able to govern permissions for it

Step 1.b: Create Persistence.xml

copy persistence.xml content from here.

this will allow automatic generation of JPA metamodels required to implement Criteria API based queries.

Step 1.c: install person model

./mvn install

./cp target/person-model-1.0.0-jar /home/flexiCore/entities

Step 2: Person Service

create a maven project with the following structure:

people service project structure

update your pom.xml from here

Step 2.a: Create Person service request objects2.a

lets define objects that will be consumed by our api:

  1. PersonCreate – this object will contain all required details for creating a person it will also inherit from BaseclassCreate object as we would like to extend Baseclass capabilities
  2. PersonUpdate – this object will extend PersonCreate object and id of the person to update
  3. PersonFilter – this object will be sent by the client when fetching people containing filtering options.
package com.flexicore.examples.request;

import com.flexicore.request.BaseclassCreate;

public class PersonCreate extends BaseclassCreate {
   private String firstName;
   private String lastName;

   public String getFirstName() {
      return firstName;
   }

   public <T extends PersonCreate> T setFirstName(String firstName) {
      this.firstName = firstName;
      return (T) this;
   }

   public String getLastName() {
      return lastName;
   }

   public <T extends PersonCreate> T setLastName(String lastName) {
      this.lastName = lastName;
      return (T) this;
   }

   @Override
   public boolean supportingDynamic() {
      return super.supportingDynamic();
   }
}
 
package com.flexicore.examples.request;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.flexicore.example.person.Person;

public class PersonUpdate extends PersonCreate{

    private String id;
    @JsonIgnore
    private Person person;

    public String getId() {
        return id;
    }

    public <T extends PersonUpdate> T setId(String id) {
        this.id = id;
        return (T) this;
    }

    @JsonIgnore
    public Person getPerson() {
        return person;
    }

    public <T extends PersonUpdate> T setPerson(Person person) {
        this.person = person;
        return (T) this;
    }
}
 
package com.flexicore.examples.request;

import com.flexicore.model.FilteringInformationHolder;

public class PersonFilter extends FilteringInformationHolder {

}
 

Step 2.b: Create Person Repository

lets define the repository that will be used to fetch and save people from database.

package com.flexicore.examples.data;

import com.flexicore.annotations.plugins.PluginInfo;
import com.flexicore.example.person.Person;
import com.flexicore.examples.request.PersonFilter;
import com.flexicore.interfaces.AbstractRepositoryPlugin;
import com.flexicore.model.QueryInformationHolder;
import com.flexicore.security.SecurityContext;

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;
import org.pf4j.Extension;
import org.springframework.stereotype.Component;


@PluginInfo(version = 1)
@Extension
@Component
public class PersonRepository extends AbstractRepositoryPlugin  {

   public List<Person> listAllPersons(PersonFilter filtering,
         SecurityContext securityContext) {
      CriteriaBuilder cb = em.getCriteriaBuilder();
      CriteriaQuery<Person> q = cb.createQuery(Person.class);
      Root<Person> r = q.from(Person.class);
      List<Predicate> preds = new ArrayList<>();
      addPersonPredicate(filtering, cb, r, preds);
      QueryInformationHolder<Person> queryInformationHolder = new QueryInformationHolder<>(filtering, Person.class, securityContext);
      return getAllFiltered(queryInformationHolder, preds, cb, q, r);
   }

   public Long countAllPersons(PersonFilter filtering,
         SecurityContext securityContext) {
      CriteriaBuilder cb = em.getCriteriaBuilder();
      CriteriaQuery<Long> q = cb.createQuery(Long.class);
      Root<Person> r = q.from(Person.class);
      List<Predicate> preds = new ArrayList<>();
      addPersonPredicate(filtering, cb, r, preds);
      QueryInformationHolder<Person> queryInformationHolder = new QueryInformationHolder<>(filtering, Person.class, securityContext);
      return countAllFiltered(queryInformationHolder, preds, cb, q, r);
   }

   static <T extends Person> void addPersonPredicate(PersonFilter filtering, CriteriaBuilder cb, Root<T> r, List<Predicate> preds) {

   }



} 
  1. the repository is annotated by
    •  @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.
  2. the repository class extends AbstractRepositoryPlugin which provides easy method for access control and out of the box methods for persisting objects
  3. the repository exposes methods for listing and counting people both are calling the addPersonPredicates which adds the required predicates , all access control predicates are automatically added when countAllFiltered and getAllFiltered are called
  4. addPersonPredicates uses JPA Criteria Api to filter data based on PersonFilter object we have created in previous phase , it is empty as we currently have not specific filters.

Step 2.c: Create Person Service

lets define the service 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.person.Person;
import com.flexicore.examples.data.PersonRepository;
import com.flexicore.examples.request.PersonCreate;
import com.flexicore.examples.request.PersonFilter;
import com.flexicore.examples.request.PersonUpdate;
import com.flexicore.interfaces.ServicePlugin;
import com.flexicore.model.Baseclass;
import com.flexicore.security.SecurityContext;
import com.flexicore.service.BaseclassNewService;
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;

@PluginInfo(version = 1)
@Component
@Extension
@Primary
public class PersonService implements ServicePlugin {

   @PluginInfo(version = 1)
   @Autowired
   private PersonRepository repository;

   @Autowired
   private BaseclassNewService baseclassNewService;


   public Person createPerson(PersonCreate personCreate,
         SecurityContext securityContext) {
      Person person = createPersonNoMerge(personCreate, securityContext);
      repository.merge(person);
      return person;
   }

   public Person createPersonNoMerge(PersonCreate personCreate,
         SecurityContext securityContext) {
      Person person = new Person(personCreate.getFirstName(), securityContext);
      updatePersonNoMerge(person, personCreate);
      return person;
   }

   public boolean updatePersonNoMerge(Person person, PersonCreate personCreate) {
      boolean update = baseclassNewService.updateBaseclassNoMerge(personCreate,person);
      if (personCreate.getFirstName() != null
            && !personCreate.getFirstName().equals(person.getFirstName())) {
         person.setFirstName(personCreate.getFirstName());
         update = true;
      }

      if (personCreate.getLastName() != null
            && !personCreate.getLastName().equals(person.getLastName())) {
         person.setLastName(personCreate.getLastName());
         update = true;
      }

      return update;
   }

   public Person updatePerson(PersonUpdate personUpdate,
         SecurityContext securityContext) {
      Person person = personUpdate.getPerson();
      if (updatePersonNoMerge(person, personUpdate)) {
         repository.merge(person);
      }
      return person;
   }

   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<Person> getAllPersons(PersonFilter personFilter,
         SecurityContext securityContext) {
      List<Person> list = listAllPersons(personFilter, securityContext);
      long count = repository.countAllPersons(personFilter, securityContext);
      return new PaginationResponse<>(list, personFilter, count);
   }

   public List<Person> listAllPersons(PersonFilter personFilter,
         SecurityContext securityContext) {
      return repository.listAllPersons(personFilter, securityContext);
   }

} 

Step 2.d: Create Person REST Service

lets define the REST service 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.person.Person;
import com.flexicore.examples.request.PersonCreate;
import com.flexicore.examples.request.PersonFilter;
import com.flexicore.examples.request.PersonUpdate;
import com.flexicore.examples.service.PersonService;

import com.flexicore.interfaces.RESTService;
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.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/Person")
@Tag(name = "Person")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
@Component
@Extension
@Primary
public class PersonRESTService implements RestServicePlugin {

   @PluginInfo(version = 1)
   @Autowired
   private PersonService service;

   @POST
   @Path("createPerson")
   @Operation(summary = "createPerson", description = "Creates Person")
   public Person createPerson(
         @HeaderParam("authenticationKey") String authenticationKey,
         PersonCreate personCreate, @Context SecurityContext securityContext) {
      return service.createPerson(personCreate, securityContext);
   }

   @PUT
   @Operation(summary = "updatePerson", description = "Updates Person")
   @Path("updatePerson")
   public Person updatePerson(
         @HeaderParam("authenticationKey") String authenticationKey,
         PersonUpdate personUpdate, @Context SecurityContext securityContext) {
      String personId = personUpdate.getId();
      Person person = personId != null ? service.getByIdOrNull(personId,
            Person.class, null, securityContext) : null;
      if (person == null) {
         throw new BadRequestException("No Person with id " + personId);
      }
      personUpdate.setPerson(person);
      return service.updatePerson(personUpdate, securityContext);
   }

   @POST
   @Operation(summary = "getAllPersons", description = "Gets All Persons Filtered")
   @Path("getAllPersons")
   public PaginationResponse<Person> getAllPersons(
         @HeaderParam("authenticationKey") String authenticationKey,
         PersonFilter personFilter, @Context SecurityContext securityContext) {
      return service.getAllPersons(personFilter, securityContext);
   }
}