= The visit module and Visit entity

:Notice: Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at. http://www.apache.org/licenses/LICENSE-2.0 . Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR  CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.


Our domain model now consists of the `PetOwner` and `Pet` entities (along with the `PetSpecies`  enum).
In this section we'll add in the `Visit` entity:

include::partial$domain.adoc[]

Also, note that the `Visit` entity is in its own module, the `visit` module.
So in this section of exercises we'll start to learn how Apache Causeway helps you keep your applications modular.



[#exercise-5-1-the-visits-module]
== Ex 5.1: The visits module

In this exercise we'll just create an empty visits module.

=== Solution

[source,bash,subs="attributes+"]
----
git checkout tags/{tag-version}/05-01-visit-module
mvn clean install
mvn -pl webapp spring-boot:run -Dspring-boot.run.jvmArguments="-Dspring.profiles.active=dev"
----

=== Tasks

To save time, just checkout the solution tag above and review the git history to see the files and classes that were created:

* A new `simpleapp-jpa-module-visit` maven module was defined in the top-level `pom.xml`

* the `pom.xml` for the new visit module itself was created, and referenced from the top-level `pom.xml` as a child `<module>`

* a `VisitModule` class was created, defining the module to Causeway as a Spring `@Configuration`:
+
[source,java]
.VisitModule.java
----
@Configuration
@Import({
        CausewayModuleExtPdfjsApplib.class,
        CausewayModuleExtFullCalendarApplib.class,
        CausewayModuleTestingFakeDataApplib.class,
        CausewayModulePersistenceJpaApplib.class,
})
@ComponentScan
@EnableJpaRepositories
@EntityScan(basePackageClasses = {VisitModule.class})
public class VisitModule implements ModuleWithFixtures {

    public static final String NAMESPACE = "visit";
    public static final String SCHEMA = "visit";

    @Override
    public FixtureScript getTeardownFixture() {
        return new TeardownFixtureJpaAbstract() {
            @Override
            protected void execute(ExecutionContext executionContext) {
            }
        };
    }
}
----

* the top-level `application.properties` was updated to ensure that the new `visit` schema is created (when running with an in-memory database)
+
[source,properties]
.application.properties
----
causeway.persistence.schema.auto-create-schemas=\
    petowner,visit,simple,...
----

* add permissions to the new "visit" namespace.
We could do this by adding a new security role, but for simplicity we'll just add to the existing role (`PetOwnerModuleSuperuserRole`), renaming it as we do:
+
[source,java]
.CustomRolesAndUsers.java
----
private static class PetClinicSuperuserRole                         // <.>
    extends AbstractRoleAndPermissionsFixtureScript {

    public static final String ROLE_NAME = "petclinic-superuser";   // <.>

    public PetClinicSuperuserRole() {
        super(ROLE_NAME, "Permission to use everything in the 'petowner' and 'visit' modules");
    }

    @Override
    protected void execute(ExecutionContext executionContext) {
        newPermissions(
                ApplicationPermissionRule.ALLOW,
                ApplicationPermissionMode.CHANGING,
                Can.of(ApplicationFeatureId.newNamespace("petowner"),
                       ApplicationFeatureId.newNamespace("visit")       // <.>
                )
        );
    }
}
----
<.> renamed
<.> renamed
<.> added


[#exercise-5-2-visit-module-dependencies]
== Ex 5.2: Visit Module Dependencies


Although we have a visit module, it currently is unaware of the pet owner module.
And similarly, the top-level application doesn't yet know about visit module.

In this exercise we'll fix this, so that:

`application` module -> `visit` module -> `petowner` module



=== Solution

[source,bash,subs="attributes+"]
----
git checkout tags/{tag-version}/05-02-visit-module-dependencies
mvn clean install
mvn -pl webapp spring-boot:run -Dspring-boot.run.jvmArguments="-Dspring.profiles.active=dev"
----

=== Tasks

First, let's consider the `visit` -> `petowner` dependency:

* in the new `visit` module's `pom.xml`, add a Maven dependency on `petowner` module:
+
[source,xml]
.visit/pom.xml
----
<dependency>
    <groupId>org.apache.causeway.starters</groupId>
    <artifactId>simpleapp-jpa-module-petowner</artifactId>
</dependency>
----
+
TIP: In your IDE you may need to reload/refresh dependencies to rebuild the classpath.

* in the `VisitModule` Causeway module, add a corresponding import on `PetOwnerModule`:
+
[source,java]
.VisitModule.java
----
@Configuration
@Import({
        PetOwnerModule.class,
        // ...
})
// ...
public class VisitModule ... { ... }
----

And next, let's consider the `application` -> `visit` dependency:

* in the webapp module, add a Maven dependency on the visit module:
+
[source,xml]
.visit/pom.xml
----
<dependency>
    <groupId>org.apache.causeway.starters</groupId>
    <artifactId>simpleapp-jpa-module-visit</artifactId>
</dependency>
----
+
The dependency on `<artifactId>simpleapp-jpa-module-petowner</artifactId>` can also be removed, due to transitivity.

* And, in the webapp's `ApplicationModule` Causeway module, add a corresponding import on `VisitModule`.
+
[source,java]
.ApplicationModule.java
----
@Configuration
@Import({
        VisitModule.class,
        SimpleModule.class,
})
@ComponentScan
public class ApplicationModule {
}
----
+
The import of `PetOwnerModule.class` can similarly be removed, due to transitivity.

Run the application and check that it starts ok.


[#exercise-5-3-visit-entitys-key-properties]
== Ex 5.3: Visit entity's key properties

Now we have a visits module, we can now add in the `Visit` entity.
We'll start just with the key properties.


=== Solution

[source,bash,subs="attributes+"]
----
git checkout tags/{tag-version}/05-03-visit-entity-key-properties
mvn clean install
mvn -pl webapp spring-boot:run -Dspring-boot.run.jvmArguments="-Dspring.profiles.active=dev"
----


=== Tasks

* add a `Visit` entity (in the `dom.visit` subpackage), declaring the `pet` and `visitAt` key properties:
+
[source,java]
.Visit.java
----
@Entity
@Table(
        schema=VisitModule.SCHEMA,
        uniqueConstraints = {
                @UniqueConstraint(name = "Visit__pet_visitAt__UNQ", columnNames = {"pet_id", "visitAt"})
        }
)
@EntityListeners(CausewayEntityListener.class)
@Named(VisitModule.NAMESPACE + ".Visit")
@DomainObject(entityChangePublishing = Publishing.ENABLED)
@DomainObjectLayout()
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@XmlJavaTypeAdapter(PersistentEntityAdapter.class)
@ToString(onlyExplicitlyIncluded = true)
public class Visit implements Comparable<Visit> {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "id", nullable = false)
    @Getter @Setter
    private Long id;

    @Version
    @Column(name = "version", nullable = false)
    @PropertyLayout(fieldSetId = "metadata", sequence = "999")
    @Getter @Setter
    private long version;

    public Visit(Pet pet, LocalDateTime visitAt) {
        this.pet = pet;
        this.visitAt = visitAt;
    }

    @ObjectSupport
    public String title() {
        return titleService.titleOf(getPet())       // <.>
                + " @ "
                + getVisitAt().format(DateTimeFormatter.ofPattern("dd-MM-yyyy HH:mm"));
    }

    @ManyToOne(optional = false)
    @JoinColumn(name = "pet_id")
    @PropertyLayout(fieldSetId = "identity", sequence = "1")
    @Getter @Setter
    private Pet pet;

    @Column(name = "visitAt", nullable = false)
    @Getter @Setter
    @PropertyLayout(fieldSetId = "identity", sequence = "2")
    private LocalDateTime visitAt;


    private final static Comparator<Visit> comparator =
            Comparator.comparing(Visit::getPet).thenComparing(Visit::getVisitAt);

    @Override
    public int compareTo(final Visit other) {
        return comparator.compare(this, other);
    }

    @Inject @Transient TitleService titleService;   // <1>
}
----
<.> uses the injected xref:refguide:applib:index/services/title/TitleService.adoc[] to obtain the title of the object, as determined by the framework.

* create a `Visit.layout.xml` layout file:
+
[source,xml]
.Visit.layout.xml
----
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<bs3:grid
        xsi:schemaLocation="https://causeway.apache.org/applib/layout/component https://causeway.apache.org/applib/layout/component/component.xsd https://causeway.apache.org/applib/layout/grid/bootstrap3 https://causeway.apache.org/applib/layout/grid/bootstrap3/bootstrap3.xsd"
        xmlns:cpt="https://causeway.apache.org/applib/layout/component"
        xmlns:bs3="https://causeway.apache.org/applib/layout/grid/bootstrap3"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <bs3:row>
        <bs3:col span="12" unreferencedActions="true">
            <cpt:domainObject bookmarking="AS_ROOT"/>
        </bs3:col>
    </bs3:row>
    <bs3:row>
        <bs3:col span="6">
            <bs3:row>
                <bs3:col span="12">
                    <bs3:tabGroup>
                        <bs3:tab name="Identity">
                            <bs3:row>
                                <bs3:col span="12">
                                    <cpt:fieldSet name="Identity" id="identity"/>
                                </bs3:col>
                            </bs3:row>
                        </bs3:tab>
                        <bs3:tab name="Other">
                            <bs3:row>
                                <bs3:col span="12">
                                    <cpt:fieldSet name="Other" id="other" unreferencedProperties="true"/>
                                </bs3:col>
                            </bs3:row>
                        </bs3:tab>
                        <bs3:tab name="Metadata">
                            <bs3:row>
                                <bs3:col span="12">
                                    <cpt:fieldSet name="Metadata" id="metadata"/>
                                </bs3:col>
                            </bs3:row>
                        </bs3:tab>
                    </bs3:tabGroup>
                </bs3:col>
                <bs3:col span="12">
                    <cpt:fieldSet name="Details" id="details"/>
                </bs3:col>
            </bs3:row>
        </bs3:col>
        <bs3:col span="6">
            <bs3:row>
                <bs3:col span="12">
                </bs3:col>
            </bs3:row>
            <bs3:tabGroup  unreferencedCollections="true">
            </bs3:tabGroup>
        </bs3:col>
    </bs3:row>
</bs3:grid>
----

* create a `Visit.columnOrder.txt` file
+
[source,text]
.Visit.columnOrder.txt
----
pet
visitAt
#id
#version
----

* download a `Visit.png` file.



Run the application, and login as `secman-admin`/`pass` to confirm that the table is created correctly using menu:Prototyping[H2 Console].
Also check that the unique index has been created correctly.

image::05-02/Visit-entity.png[]



[#exercise-5-4-book-visit-action]
== Ex 5.4: "Book Visit" action

We now want to extend our domain model so that ``Visit``s to be created.

However, there's a problem:

* we would like that behaviour to reside on `PetOwner` (say)
* however the pet owner module doesn't know about visits.

We can see this in the domain model:

include::partial$domain.adoc[]

Causeway's solution to this is to allow the visit module to define behaviour, but have the behaviour seem to belong to the `Pet` entity, at least so far as the user interface is concerned.
This is done using a xref:userguide::mixins.adoc[mixin].

Because `Visit` is its own root entity, we're also going to need a repository to be able to look them up for a given `Pet`.

In this exercise we'll define the repository, and create the "book visit" mixin action (also sometimes called a contributed action.
We'll also create a mixin _collection_ to be able to view the visits from a ``PetOwner``'s UI, too.


=== Solution

[source,bash,subs="attributes+"]
----
git checkout tags/{tag-version}/05-04-book-visit-action
mvn clean install
mvn -pl webapp spring-boot:run -Dspring-boot.run.jvmArguments="-Dspring.profiles.active=dev"
----


=== Tasks

* create the `VisitRepository`, using Spring Data:
+
[source,java]
.VisitRepository.java
----
import org.springframework.data.repository.Repository;

// ...

public interface VisitRepository extends Repository<Visit, Long> {

    @Query("select v from Visit v where v.pet.petOwner = :petOwner")
    List<Visit> findByPetOwner(PetOwner petOwner);

}
----

* define the "book visit" mixin action on `PetOwner`, in the _visit_ module.
+
NOTE: we simply use a datetime to capture when the visit occurs.
This isn't particularly realistic, we know - there would probably be a domain concept such as `AppointmentSlot`.
+
[source,java]
.PetOwner_bookVisit.java
----
@Action                                                     // <.>
@ActionLayout(associateWith = "visits")                     // <.>
@RequiredArgsConstructor
public class PetOwner_bookVisit {                           // <.>

    private final PetOwner petOwner;                        // <.>

    @MemberSupport
    public PetOwner act(Pet pet, LocalDateTime visitAt) {
        Visit visit = new Visit(pet, visitAt);
        repositoryService.persistAndFlush(visit);           // <.>
        return petOwner;
    }
    @MemberSupport
    public Set<Pet> choices0Act() {                         // <.>
        return petOwner.getPets();
    }
    @MemberSupport
    public Pet default0Act() {                              // <.>
        Set<Pet> pets = petOwner.getPets();
        return pets.size() == 1 ? pets.iterator().next() : null;
    }
    @MemberSupport
    public LocalDateTime default1Act() {                    // <7>
        return officeHoursTomorrow();
    }
    @MemberSupport
    public String validate1Act(LocalDateTime visitAt) {
        if (visitAt.isBefore(officeHoursTomorrow())) {
            return "Must book in the future";
        }
        return null;
    }

    private LocalDateTime officeHoursTomorrow() {
        return clockService.getClock().nowAsLocalDate().atStartOfDay().plusDays(1).plusHours(9);
    }


    @Inject ClockService clockService;
    @Inject RepositoryService repositoryService;            // <5>
}
----
<.> indicates that this class is a mixin action
<.> anticipates there being a "visits" collection also
<.> the name of the contributed action is inferred from the mixin's class name
<.> the type to which this mixin is being contributed, that is, `PetOwner`
<.> injected xref:refguide:applib:index/services/repository/RepositoryService.adoc[] acts as a facade to the database for all entities.
For querying it's usually worth defining a custom repository.
<.> the xref:refguide:applib-methods:prefixes.adoc#choices[choices...()] supporting method provides programmatic set of choices for a parameter, in this case for the 0^th^ parameter `Pet`, rendered as a drop-down list.
<.> the xref:refguide:applib-methods:prefixes.adoc#default[default...()] supporting method returns a default value for a parameter.

* define the "visits" mixin collection on `PetOwner`, in the _visit_ module.
+
[source,java]
.PetOwner_visits.java
----
@Collection                                     // <.>
@RequiredArgsConstructor
public class PetOwner_visits {

    private final PetOwner petOwner;            // <.>

    @MemberSupport
    public List<Visit> coll() {
        return visitRepository.findByPetOwner(petOwner);
    }

    @Inject VisitRepository visitRepository;
}
----
<.> indicates that this class is a mixin collection
<.> the type to which this mixin is being contributed, that is, `PetOwner`

* update ``PetOwner``'s `.layout.xml`, to indicate where the contributed `visits` collection should be placed:
+
[source,xml]
.PetOwner.layout.xml
----
<bs3:col span="12">
    <bs3:row>
        <bs3:col span="12">
            <cpt:collection id="pets"/>
        </bs3:col>
    </bs3:row>
    <bs3:row>
        <bs3:col span="12">
            <cpt:collection id="visits"/>
        </bs3:col>
    </bs3:row>
    <cpt:fieldSet name="Content" id="content">
        ...
    </cpt:fieldSet>
</bs3:col>
----
+
[NOTE]
====
Strictly speaking, updating the `.layout.xml` _does_ make the `petowner` module aware of `visit` module, albeit it in a very soft way.

Alternatively, the `.layout.xml` can be left untouched in which case the contributed `visits` collection will be rendered in the same place as any other "unreferencedCollections".
====



