This is an automated email from the ASF dual-hosted git repository. danhaywood pushed a commit to branch CAUSEWAY-2873 in repository https://gitbox.apache.org/repos/asf/causeway.git
commit bebe78fc002e2dfbf77fe79fabb8ac96f2a02871 Author: Dan Haywood <[email protected]> AuthorDate: Wed May 22 23:21:27 2024 +0100 CAUSEWAY-2873: updates petclinic tutorial up to 2.5 incl. --- .../petclinic/pages/010-getting-started.adoc | 40 ++- .../petclinic/pages/020-the-petclinic-domain.adoc | 317 ++++++++++++++++++++- .../partials/fixture-scripts/api-and-usage.adoc | 1 + 3 files changed, 330 insertions(+), 28 deletions(-) diff --git a/antora/components/tutorials/modules/petclinic/pages/010-getting-started.adoc b/antora/components/tutorials/modules/petclinic/pages/010-getting-started.adoc index ec13043e16..a4a1e6e8a8 100644 --- a/antora/components/tutorials/modules/petclinic/pages/010-getting-started.adoc +++ b/antora/components/tutorials/modules/petclinic/pages/010-getting-started.adoc @@ -10,11 +10,11 @@ You'll need: * Java 11 + -Apache Causeway should work up to at least Java 15, but stick with Java 11 for now. +Apache Causeway works up to at least Java 21, but stick with Java 11 for now. -* Maven 3.6.x +* Maven 3.9.x + -Maven 3.6.x or later is recommended. +Maven 3.9.x or later is recommended. * git + @@ -25,14 +25,14 @@ This has multiple tags for the various checkpoints so you can pick up the tutori + The Apache Causeway website has detailed documentation for setting up to use xref:setupguide:intellij:about.adoc[IntelliJ] or xref:setupguide:eclipse:about.adoc[Eclipse]. + -For this tutorial, make sure that your IDE is configured to support Lombok. +For this tutorial, also make sure that your IDE is configured to support Lombok. [#exercise-1-1-starter-apps-clone-the-repo] == Ex 1.1: Starter apps / clone the repo -Apache Causeway provides a two starter apps, xref:docs:starters:helloworld.adoc[HelloWorld] and xref:docs:starters:simpleapp.adoc[SimpleApp]. +Apache Causeway provides two starter apps, xref:docs:starters:helloworld.adoc[HelloWorld] and xref:docs:starters:simpleapp.adoc[SimpleApp]. These are identical in terms of functionality, but the simpleapp provides more structure and includes example tests. NOTE: there are JPA and JDO variants of the starter apps. @@ -40,7 +40,6 @@ This tutorial uses JPA as it is the more commonly used persistence mechanism. We start the tutorial simply with a copy of xref:docs:starters:simpleapp.adoc[SimpleApp]; in subsequent exercises we'll refactor and build upon it to morph it into the petclinic domain. - * Clone the repo: + [source,bash] @@ -59,6 +58,16 @@ This will make it easier to inspect differences between different tags. git checkout tags/01-01-starter-app mvn clean install ---- ++ +[NOTE] +==== +The code at this tag was created by copying the `v2-jpa` tag of xref:docs:starters:simpleapp.adoc[SimpleApp]: + +[source,bash] +---- +curl https://codeload.github.com/apache/causeway-app-simpleapp/zip/v2-jpa | jar xv +---- +==== * run the app: + @@ -93,13 +102,11 @@ Running from the command line isn't ideal, so * load the project into your IDE as a Maven project, build and run. -* The app is a Spring boot application, so locate the class with a main, and run. +* The app is a Spring boot application, so locate the class with a `main()` method, and run. * alternatively, your IDE might also have specialised support for Spring Boot apps, so run the app that way if you wish. -* with the IDE load - -If you want to go deeper, open up the xref:docs:starters:simpleapp.adoc[page describing the SimpleApp] and start to explore the xref:docs:starters:simpleapp.adoc#structure-of-the-app[structure of the app] files. +If you want to go deeper, use the xref:docs:starters:simpleapp.adoc[Simple App docs] to xref:docs:starters:simpleapp.adoc#structure-of-the-app[explore the structure] of the application. @@ -111,10 +118,9 @@ Apache Causeway is an implementation of the _naked objects pattern_, which means [TIP] ==== An ORM such as JPA (EclipseLink or Hibernate) maps domain objects into an RDBMS or other datastore. -You can think of Apache Causeway (and naked objects) similarly, but it's an OIM - an object _interface_ mapper. -It maps to the UI layer rather than the persistence layer. +Apache Causeway does a similar trick, but in the opposite direction: it maps the domain objects to the UI layer. -Common to both ORMs and OIMs is an internal metamodel; this is where much of the power comes from. +Common to both is an internal metamodel, which is where much of the power comes from. ==== We can explore this by looking at the classes provided by the starter app: @@ -167,7 +173,7 @@ Typically this is static and in the same package as the class; see `SimpleObject *Mini-Exercise*: -(No solution is provided for this exercise). +(no solution is provided for this exercise). * replace the `@Title` annotation with a `title()` method: + @@ -190,7 +196,7 @@ In many cases the details of that representation can be inferred directly from t For example the label of a field for an object's property (eg `SimpleObject#name`) can be derived directly from the name of the object property itself (`getName()`). In the absence of other metadata, Apache Causeway will render a domain object with its properties to the left-hand side and its collections (if any) to the right. -The order of these properties and collections can be specified using the `@PropertyLayout` annotation and the `@CollectionLayout` annotation. +The order of these properties and collections can be specified using the xref:refguide:applib:index/annotation/PropertyLayout.adoc[@PropertyLayout] annotation and the xref:refguide:applib:index/annotation/CollectionLayout.adoc[@CollectionLayout] annotation. There are other annotations to group properties together and to associate action buttons with either properties or collections. The downside of using annotations is that changing the layout requires that the application be restarted, and certain more complex UIs, such as multi-columns or tab groups are difficult or impossible to express. @@ -200,6 +206,8 @@ This is modelled upon bootstrap and so supports arbitrary rows and columns as we *Mini-Exercise*: +(no solution is provided for this exercise). + * locate the `SimpleObject.layout.xml` file * compare the structure of the layout file to that of the rendered object * change the file, eg the relative widths of the columns @@ -220,6 +228,8 @@ In a similar fashion, the actions of the various domain services are grouped int *Mini-Exercise*: +(no solution is provided for this exercise). + * locate the `menubars.layout.xml` file * compare the structure of the layout file to that of the rendered menu bar * change the file, eg reorder menu items or create new menus diff --git a/antora/components/tutorials/modules/petclinic/pages/020-the-petclinic-domain.adoc b/antora/components/tutorials/modules/petclinic/pages/020-the-petclinic-domain.adoc index bd3033c4a4..72d06a82ee 100644 --- a/antora/components/tutorials/modules/petclinic/pages/020-the-petclinic-domain.adoc +++ b/antora/components/tutorials/modules/petclinic/pages/020-the-petclinic-domain.adoc @@ -29,36 +29,327 @@ Some of the use cases we might want to support include: This tutorial has worked solutions for all of these. [#exercise-2-1-refactor-simpleobject-to-petowner] -== Ex 2.1: Refactor `SimpleObject` to `PetOwner` +== Ex 2.1: Create a new `pets` module with `PetOwner` + +Using the `simple` module as a guide, we'll create a new `petowners` module and create a `PetOwner` and `PetOwners` domain service as reference. +(We leave the `simple` module untouched, to use as a reference). -To start with, let's rename the `SimpleObject` entity to `PetOwner` === Solution [source,bash] ---- -git checkout tags/02-01-renames-SimpleObject-to-PetOwner +git checkout tags/02-01-copies-SimpleObject-to-PetOwner mvn clean install mvn -pl spring-boot:run ---- === Tasks -Just checkout the solution above and review the git history to see the changes that have already been made. -These include: - -* Domain classes renamed (along with corresponding tests) +To save time, just checkout the solution tag above and review the git history to see the classes that were copied and renamed: -** `SimpleObject` entity -> `PetOwner` -** `SimpleObjects` domain service -> `PetOwners` +* `dom.petowner` package: +** `SimpleObject` -> `PetOwner` (entity) ++ +plus related `.layout.xml`, `.png` and `.columnOrder.txt` files +** `SimpleObjects` -> `PetOwners` (domain service) ** `SimpleObjectRepository` repository service -> `PetOwnerRepository` +** `SimpleModule` -> `PetOwnerModule` (module) +* `fixture` package: +** `PetOwner_persona` (fixture script) +* `types` package +** `Name` (meta annotation) +** `Notes` (meta annotation) +* root package of module +** `PetOwnerModule` (Spring `@Configuration`, aka module class) + +* In the `PetOwnerModule`, the namespace and schema constants were changed: ++ +[source, java] +.PetOwnerModule.java +---- +// ... +public class PetOwnerModule implements ModuleWithFixtures { + + public static final String NAMESPACE = "petowner"; + public static final String SCHEMA = "petowner"; + + // ... +} +---- + +* in `application.properties`, the "petowner" schema was also added to the xref:refguide:config:sections/causeway.persistence.schema.adoc#causeway.persistence.schema.auto-create-schemas[causeway.persistence.schema.auto-create-schemas] config property: ++ +[source,properties] +.application.properties +---- +causeway.persistence.schema.auto-create-schemas=petowner,simple,... +---- + +Resource classes were also copied. + +In addition, the new Maven module was defined in the top-level `pom.xml`, and included in `<modules>`. + +Confirm that the code still builds: + +[source,bash] +---- +mvn install +---- + +However, if you run the app confirm that it is still showing just the original `simpleapp` module: + +[source,bash] +---- +mvn -pl webapp spring-boot:run +---- + +We'll fix this in the next exercise. + + +[#exercise-2-2-configure-the-app-to-include-the-petowner-module] +== Ex 2.2: Configure the app to include the `petowner` module + +We have our new `petowner` module, but we're not actually using it; if we run the application we still see the original `simpleapp` module. + +[source,bash] +---- +mvn -pl webapp spring-boot:run +---- -** Infrastructure classes renamed +In this exercise we'll update the app to include `petowners` as well. -** `SimpleModule` -> `PetsModule` -** `SimpleApp` -> `PetClinicApp` +=== Solution + +[source,bash] +---- +git checkout tags/02-02-configure-the-app-to-include-petowner +mvn clean install +mvn -pl spring-boot:run +---- + +=== Tasks + +Rename the main class that acts as the entry point for the app (annotated with `@@SpringBootApplication`): + +* locate the `SimpleApp` class and rename to `PetClinicApp` + +* Also update classes that reference this type (in particular, in `pom.xml`). ++ +Your IDE can probably do this for you automatically. + +The `PetClinicApp` main class references the `AppManifest`, a top-level Spring `@Configuration` which imports all the other classes. + +* Inspect the `AppManifest` class: most will be ``CausewayXxxModule``s +* note that it also imports the `ApplicationModule`, which defines your own modules which make up the application + +We want to update `ApplicationModule` to reference `PetOwnerModule`. +We need to change both Maven and then Spring: -Build and run the application (note that the main class has changed) to make sure it still runs fine. +* in the `pom.xml` (of the `webapp` module), add in a dependency to the `petowner` module: ++ +[source,xml] +.webapp/pom.xml +---- +<dependencies> + + <dependency> + <groupId>${project.groupId}</groupId> + <artifactId>simpleapp-jpa-module-petowner</artifactId> + </dependency> + ... +<dependencies> +---- ++ +You might need to reimport or refresh so that your IDE rebuilds its classpath. + +* in the `ApplicationModule`, update the `@Import` to reference the `PetOwnerModule`: ++ +[source,java] +.Application.java +---- +@Configuration +@Import({ + PetOwnerModule.class, //<.> + SimpleModule.class, +}) +@ComponentScan +public class ApplicationModule { + +} +---- +<.> add this import + +Run up the application using the IDE or from maven command line. +Although everything looks good, in fact you won't see any change. +That's because the simpleapp starter app includes the xref:security:secman:about.adoc[SecMan] security module, for users, roles and permissions. +We'll fix this in the next exercise. + + +[#exercise-2-3-add-security-role-for-petowner-module] +== Ex 2.3: Add security role for the `petowner` module + +In this exercise we'll add a security role to grant access to our new `petowner` module, and we'll make sure that our "sven" demo user is a member of this role. + +=== Solution + +[source,bash] +---- +git checkout tags/02-03-add-security-role-for-petowner +mvn clean install +mvn -pl spring-boot:run +---- + +=== Tasks + +The starter app is configured to use an in-memory database. +The xref:security:secman:about.adoc[SecMan] roles and permissions are therefore set up each time the applicatoin is bootstrapped, using a fixture scripts. +We'll learn more about fixture scripts and their role in testing in a later exercise. +For now, all that's necessary to know is that we can use them to set up the roles. + +* Locate the `CustomRolesAndUsers` fixture script class + +* within it, copy the existing `SimpleModuleSuperuserRole` to create a similar `PetOwnerModuleSuperuserRole`: ++ +[source,java] +.CustomRolesAndUsers.java +---- +private static class PetOwnerModuleSuperuserRole + extends AbstractRoleAndPermissionsFixtureScript { + + public static final String ROLE_NAME = "petowner-superuser"; + + public PetOwnerModuleSuperuserRole() { + super(ROLE_NAME, "Permission to use everything in the 'petowner' module"); + } + + @Override + protected void execute(ExecutionContext executionContext) { + newPermissions( + ApplicationPermissionRule.ALLOW, + ApplicationPermissionMode.CHANGING, + Can.of(ApplicationFeatureId.newNamespace("petowner")) + ); + } +} +---- + +* further down in the same class, update the fixture script for the "sven" end-user, and add them into our new role: ++ +[source,java] +.CustomRolesAndUsers.java +---- +private static class SvenUser extends AbstractUserAndRolesFixtureScript { + ... + + private static class RoleSupplier implements Supplier<Can<String>> { + @Override + public Can<String> get() { + return Can.of( + causewayConfiguration.getExtensions().getSecman().getSeed().getRegularUser().getRoleName(), // built-in stuff + PetOwnerModuleSuperuserRole.ROLE_NAME, // <.> + SimpleModuleSuperuserRole.ROLE_NAME + ); + } + @Inject CausewayConfiguration causewayConfiguration; + } +} +---- +<.> reference new role. + +* and in the `execute()` method at top of the class, remember to include the call to the new fixture script: ++ +[source,java] +.CustomRolesAndUsers.java +---- +@Override +protected void execute(ExecutionContext executionContext) { + executionContext.executeChildren(this, + new SimpleModuleSuperuserRole(), + new PetOwnerModuleSuperuserRole(), // <.> + new SvenUser()); +} +---- +<.> Remember to call the new module + + +Now run the application from the IDE or Maven: + +* The menubar should now show a new "Other" menu - these are the now-visible actions arising from the `PetOwners` domain service. + +* Create a new `PetOwner` using "Other > Create". ++ +The resultant object will look identical in structure to a `SimpleObject`, but you can confirm that it is indeed an instance of a `PetOwner` by hovering over the title or icon and checking the tooltip. + +The fact that the `PetOwners` domain service's actions are displayed automatically is an important principle of the _naked objects pattern_ as implemented by Apache Causeway. + +[#exercise-2-4-update-menubar-for-PetOwners] +== Ex 2.4: Update menubar for PetOwners + +Let's now update the menu bar so that the actions to create ``PetOwner``s live under an appropriately named menu, "Pet Owners". + +=== Solution + +[source,bash] +---- +git checkout tags/02-04-update-menubar-for-petowners +mvn clean install +mvn -pl spring-boot:run +---- + +=== Tasks + +* Locate the `menubars.layout.xml` file. ++ +You should find it in `src/main/resources` (in the `webapp` module). + +* Add in the following XML, under the `<mb3:primary>` tag: ++ +[source,xml] +.menubars.layout.xml +---- +<mb3:primary> + <mb3:menu> + <mb3:named>Pet Owners</mb3:named> + <mb3:section> + <mb3:serviceAction objectType="petowner.PetOwners" id="create"/> + <mb3:serviceAction objectType="petowner.PetOwners" id="findByName"/> + <mb3:serviceAction objectType="petowner.PetOwners" id="findByNameLike"/> + <mb3:serviceAction objectType="petowner.PetOwners" id="listAll"/> + </mb3:section> + </mb3:menu> +... +</mb3:primary> +---- ++ +The `objectType` attribute corresponds to the `@Named` value of the `PetOwners` domain service (its "logical type name"), while the `id` attribute matches the method name of each action. + +Run the application; the menubar should be updated correctly. + + +[#exercise-2-5-setup-demo-PetOwners] +== Ex 2.5: Set up demo PetOwners + +xref:testing:fixtures:about.adoc#fixture-scripts[Fixture scripts] are used for both prototyping and for integration testing, allowing us to quickly create demo data in the H2 in-memory database. + +The xref:testing:fixtures:about.adoc#personas-and-builders[persona] pattern lets us define example data in an enum (the "what"), while a companion builder uses that data to set up the corresponding domain entities (the "how"). + +In this exercise we'll modify the `PetOwners_persona` (copied from `SimpleObjects_persona` earlier) to create more realistic data. + + +=== Solution + +[source,bash] +---- +git checkout tags/02-05-setup-demo-petowners +mvn clean install +mvn -pl spring-boot:run +---- + +=== Tasks +* locate the `PetOwners_persona` class +* refactor the enum constants and values to create 10 realistic pet owner names +* (optional) we'll use the associated `.pdf` to store a "veterinary clinic service agreement" for each pet owner. diff --git a/testing/fixtures/adoc/modules/fixtures/partials/fixture-scripts/api-and-usage.adoc b/testing/fixtures/adoc/modules/fixtures/partials/fixture-scripts/api-and-usage.adoc index 43b1160552..86b26e697f 100644 --- a/testing/fixtures/adoc/modules/fixtures/partials/fixture-scripts/api-and-usage.adoc +++ b/testing/fixtures/adoc/modules/fixtures/partials/fixture-scripts/api-and-usage.adoc @@ -192,6 +192,7 @@ This was demonstrated in the <<fixturescript, previous section>> The latter use case is much less frequently used, but can be helpful for example in demos, where the number of objects can be specified in the `parameters` parameter of the xref:refguide:testing:index/fixtures/applib/fixturescripts/FixtureScripts.adoc#runFixtureScript_String_String[run fixture script] action. +[#personas-and-builders] == Personas and Builders Good integration tests are probably the best way to understand the behaviour of the domain model: better, even, than reading the code itself.
