From 55cbdd505aa137ff7cd3f6607c41825124aca811 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Tue, 15 Jun 2021 11:00:20 +0100 Subject: [PATCH] Upgrade the samples to Spring Boot 2.4.x Closes gh-723 --- samples/junit5/build.gradle | 15 +---- samples/rest-assured/build.gradle | 18 ++--- samples/rest-notes-slate/build.gradle | 17 ++--- samples/rest-notes-spring-data-rest/pom.xml | 13 +++- .../rest-notes-spring-hateoas/build.gradle | 19 ++---- .../ExceptionSupressingErrorAttributes.java | 5 +- .../com/example/notes/IndexController.java | 12 ++-- .../example/notes/NestedContentResource.java | 36 ---------- .../src/main/java/com/example/notes/Note.java | 7 +- .../java/com/example/notes/NoteInput.java | 12 ++-- .../com/example/notes/NotePatchInput.java | 12 ++-- .../com/example/notes/NoteRepository.java | 5 +- .../NoteRepresentationModelAssembler.java | 67 +++++++++++++++++++ .../example/notes/NoteResourceAssembler.java | 54 --------------- .../com/example/notes/NotesController.java | 46 ++++++------- .../com/example/notes/NullOrNotBlank.java | 4 +- .../notes/ResourceDoesNotExistException.java | 4 +- .../notes/RestNotesControllerAdvice.java | 8 +-- .../example/notes/RestNotesSpringHateoas.java | 2 +- .../src/main/java/com/example/notes/Tag.java | 5 +- .../main/java/com/example/notes/TagInput.java | 8 +-- .../java/com/example/notes/TagPatchInput.java | 8 +-- .../java/com/example/notes/TagRepository.java | 4 +- .../TagRepresentationModelAssembler.java | 64 ++++++++++++++++++ .../example/notes/TagResourceAssembler.java | 54 --------------- .../com/example/notes/TagsController.java | 46 ++++++------- samples/testng/build.gradle | 19 ++---- 27 files changed, 255 insertions(+), 309 deletions(-) delete mode 100644 samples/rest-notes-spring-hateoas/src/main/java/com/example/notes/NestedContentResource.java create mode 100644 samples/rest-notes-spring-hateoas/src/main/java/com/example/notes/NoteRepresentationModelAssembler.java delete mode 100644 samples/rest-notes-spring-hateoas/src/main/java/com/example/notes/NoteResourceAssembler.java create mode 100644 samples/rest-notes-spring-hateoas/src/main/java/com/example/notes/TagRepresentationModelAssembler.java delete mode 100644 samples/rest-notes-spring-hateoas/src/main/java/com/example/notes/TagResourceAssembler.java diff --git a/samples/junit5/build.gradle b/samples/junit5/build.gradle index 27776c38..d25c71bf 100644 --- a/samples/junit5/build.gradle +++ b/samples/junit5/build.gradle @@ -1,19 +1,10 @@ -buildscript { - repositories { - mavenCentral() - } - dependencies { - classpath 'org.springframework.boot:spring-boot-gradle-plugin:2.1.9.RELEASE' - } -} - plugins { + id "eclipse" + id "java" id "org.asciidoctor.jvm.convert" version "3.3.2" + id "org.springframework.boot" version "2.4.7" } -apply plugin: 'java' -apply plugin: 'org.springframework.boot' -apply plugin: 'eclipse' apply plugin: 'io.spring.dependency-management' repositories { diff --git a/samples/rest-assured/build.gradle b/samples/rest-assured/build.gradle index f1ed5ab8..0ab61172 100644 --- a/samples/rest-assured/build.gradle +++ b/samples/rest-assured/build.gradle @@ -1,19 +1,10 @@ -buildscript { - repositories { - mavenCentral() - } - dependencies { - classpath 'org.springframework.boot:spring-boot-gradle-plugin:2.1.9.RELEASE' - } -} - plugins { + id "eclipse" + id "java" id "org.asciidoctor.jvm.convert" version "3.3.2" + id "org.springframework.boot" version "2.4.7" } -apply plugin: 'java' -apply plugin: 'org.springframework.boot' -apply plugin: 'eclipse' apply plugin: 'io.spring.dependency-management' repositories { @@ -44,6 +35,9 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-web' testImplementation 'io.rest-assured:rest-assured:3.0.2' + testImplementation('org.junit.vintage:junit-vintage-engine') { + exclude group: 'org.hamcrest', module: 'hamcrest-core' + } testImplementation 'org.springframework.boot:spring-boot-starter-test' testImplementation 'org.springframework.restdocs:spring-restdocs-restassured' } diff --git a/samples/rest-notes-slate/build.gradle b/samples/rest-notes-slate/build.gradle index a357e7ee..5ad049a4 100644 --- a/samples/rest-notes-slate/build.gradle +++ b/samples/rest-notes-slate/build.gradle @@ -1,15 +1,9 @@ -buildscript { - repositories { - mavenCentral() - } - dependencies { - classpath 'org.springframework.boot:spring-boot-gradle-plugin:2.1.9.RELEASE' - } +plugins { + id "eclipse" + id "java" + id "org.springframework.boot" version "2.4.7" } -apply plugin: 'java' -apply plugin: 'org.springframework.boot' -apply plugin: 'eclipse' apply plugin: 'io.spring.dependency-management' repositories { @@ -38,6 +32,9 @@ dependencies { runtimeOnly 'org.atteo:evo-inflector:1.2.1' testImplementation 'com.jayway.jsonpath:json-path' + testImplementation('org.junit.vintage:junit-vintage-engine') { + exclude group: 'org.hamcrest', module: 'hamcrest-core' + } testImplementation 'org.springframework.boot:spring-boot-starter-test' testImplementation 'org.springframework.restdocs:spring-restdocs-mockmvc' } diff --git a/samples/rest-notes-spring-data-rest/pom.xml b/samples/rest-notes-spring-data-rest/pom.xml index fccf9a86..3ed3bf35 100644 --- a/samples/rest-notes-spring-data-rest/pom.xml +++ b/samples/rest-notes-spring-data-rest/pom.xml @@ -11,7 +11,7 @@ org.springframework.boot spring-boot-starter-parent - 2.1.9.RELEASE + 2.4.7 @@ -37,6 +37,17 @@ runtime + + org.junit.vintage + junit-vintage-engine + test + + + org.hamcrest + hamcrest-core + + + org.springframework.boot spring-boot-starter-test diff --git a/samples/rest-notes-spring-hateoas/build.gradle b/samples/rest-notes-spring-hateoas/build.gradle index 666441fb..449997d2 100644 --- a/samples/rest-notes-spring-hateoas/build.gradle +++ b/samples/rest-notes-spring-hateoas/build.gradle @@ -1,19 +1,10 @@ -buildscript { - repositories { - mavenCentral() - } - dependencies { - classpath 'org.springframework.boot:spring-boot-gradle-plugin:2.1.9.RELEASE' - } -} - plugins { + id "eclipse" + id "java" id "org.asciidoctor.jvm.convert" version "3.3.2" + id "org.springframework.boot" version "2.4.7" } -apply plugin: 'java' -apply plugin: 'org.springframework.boot' -apply plugin: 'eclipse' apply plugin: 'io.spring.dependency-management' repositories { @@ -43,12 +34,16 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-data-jpa' implementation 'org.springframework.boot:spring-boot-starter-hateoas' + implementation 'org.springframework.boot:spring-boot-starter-validation' runtimeOnly 'com.h2database:h2' runtimeOnly 'org.atteo:evo-inflector:1.2.1' testImplementation 'com.jayway.jsonpath:json-path' testImplementation 'org.assertj:assertj-core' + testImplementation('org.junit.vintage:junit-vintage-engine') { + exclude group: 'org.hamcrest', module: 'hamcrest-core' + } testImplementation 'org.springframework.boot:spring-boot-starter-test' testImplementation 'org.springframework.restdocs:spring-restdocs-mockmvc' } diff --git a/samples/rest-notes-spring-hateoas/src/main/java/com/example/notes/ExceptionSupressingErrorAttributes.java b/samples/rest-notes-spring-hateoas/src/main/java/com/example/notes/ExceptionSupressingErrorAttributes.java index 8ac9be55..d7725a51 100644 --- a/samples/rest-notes-spring-hateoas/src/main/java/com/example/notes/ExceptionSupressingErrorAttributes.java +++ b/samples/rest-notes-spring-hateoas/src/main/java/com/example/notes/ExceptionSupressingErrorAttributes.java @@ -18,6 +18,7 @@ package com.example.notes; import java.util.Map; +import org.springframework.boot.web.error.ErrorAttributeOptions; import org.springframework.boot.web.servlet.error.DefaultErrorAttributes; import org.springframework.stereotype.Component; import org.springframework.web.context.request.RequestAttributes; @@ -28,8 +29,8 @@ class ExceptionSupressingErrorAttributes extends DefaultErrorAttributes { @Override public Map getErrorAttributes(WebRequest webRequest, - boolean includeStackTrace) { - Map errorAttributes = super.getErrorAttributes(webRequest, includeStackTrace); + ErrorAttributeOptions options) { + Map errorAttributes = super.getErrorAttributes(webRequest, options); errorAttributes.remove("exception"); Object message = webRequest.getAttribute("javax.servlet.error.message", RequestAttributes.SCOPE_REQUEST); if (message != null) { diff --git a/samples/rest-notes-spring-hateoas/src/main/java/com/example/notes/IndexController.java b/samples/rest-notes-spring-hateoas/src/main/java/com/example/notes/IndexController.java index bd3e6181..25f69dcd 100644 --- a/samples/rest-notes-spring-hateoas/src/main/java/com/example/notes/IndexController.java +++ b/samples/rest-notes-spring-hateoas/src/main/java/com/example/notes/IndexController.java @@ -1,5 +1,5 @@ /* - * Copyright 2014 the original author or authors. + * Copyright 2014-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,20 +16,20 @@ package com.example.notes; -import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo; +import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.linkTo; -import org.springframework.hateoas.ResourceSupport; +import org.springframework.hateoas.RepresentationModel; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/") -public class IndexController { +class IndexController { @RequestMapping(method=RequestMethod.GET) - public ResourceSupport index() { - ResourceSupport index = new ResourceSupport(); + public RepresentationModel index() { + RepresentationModel index = new RepresentationModel<>(); index.add(linkTo(NotesController.class).withRel("notes")); index.add(linkTo(TagsController.class).withRel("tags")); return index; diff --git a/samples/rest-notes-spring-hateoas/src/main/java/com/example/notes/NestedContentResource.java b/samples/rest-notes-spring-hateoas/src/main/java/com/example/notes/NestedContentResource.java deleted file mode 100644 index 4294eed1..00000000 --- a/samples/rest-notes-spring-hateoas/src/main/java/com/example/notes/NestedContentResource.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2014 the original author or authors. - * - * Licensed 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 - * - * https://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. - */ - -package com.example.notes; - -import org.springframework.hateoas.ResourceSupport; -import org.springframework.hateoas.Resources; - -import com.fasterxml.jackson.annotation.JsonUnwrapped; - -public class NestedContentResource extends ResourceSupport { - - private final Resources nested; - - public NestedContentResource(Iterable toNest) { - this.nested = new Resources(toNest); - } - - @JsonUnwrapped - public Resources getNested() { - return this.nested; - } -} diff --git a/samples/rest-notes-spring-hateoas/src/main/java/com/example/notes/Note.java b/samples/rest-notes-spring-hateoas/src/main/java/com/example/notes/Note.java index a4f037ad..46b349da 100644 --- a/samples/rest-notes-spring-hateoas/src/main/java/com/example/notes/Note.java +++ b/samples/rest-notes-spring-hateoas/src/main/java/com/example/notes/Note.java @@ -1,5 +1,5 @@ /* - * Copyright 2014 the original author or authors. + * Copyright 2014-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,8 +24,6 @@ import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.ManyToMany; -import com.fasterxml.jackson.annotation.JsonIgnore; - @Entity public class Note { @@ -40,7 +38,6 @@ public class Note { @ManyToMany private List tags; - @JsonIgnore public long getId() { return id; } @@ -65,7 +62,6 @@ public class Note { this.body = body; } - @JsonIgnore public List getTags() { return tags; } @@ -73,4 +69,5 @@ public class Note { public void setTags(List tags) { this.tags = tags; } + } diff --git a/samples/rest-notes-spring-hateoas/src/main/java/com/example/notes/NoteInput.java b/samples/rest-notes-spring-hateoas/src/main/java/com/example/notes/NoteInput.java index b2a8bccb..d5fd5c86 100644 --- a/samples/rest-notes-spring-hateoas/src/main/java/com/example/notes/NoteInput.java +++ b/samples/rest-notes-spring-hateoas/src/main/java/com/example/notes/NoteInput.java @@ -1,5 +1,5 @@ /* - * Copyright 2014 the original author or authors. + * Copyright 2014-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,7 +25,7 @@ import javax.validation.constraints.NotBlank; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; -public class NoteInput { +class NoteInput { @NotBlank private final String title; @@ -35,23 +35,23 @@ public class NoteInput { private final List tagUris; @JsonCreator - public NoteInput(@JsonProperty("title") String title, + NoteInput(@JsonProperty("title") String title, @JsonProperty("body") String body, @JsonProperty("tags") List tagUris) { this.title = title; this.body = body; this.tagUris = tagUris == null ? Collections.emptyList() : tagUris; } - public String getTitle() { + String getTitle() { return title; } - public String getBody() { + String getBody() { return body; } @JsonProperty("tags") - public List getTagUris() { + List getTagUris() { return this.tagUris; } diff --git a/samples/rest-notes-spring-hateoas/src/main/java/com/example/notes/NotePatchInput.java b/samples/rest-notes-spring-hateoas/src/main/java/com/example/notes/NotePatchInput.java index b163e673..6d69bfa8 100644 --- a/samples/rest-notes-spring-hateoas/src/main/java/com/example/notes/NotePatchInput.java +++ b/samples/rest-notes-spring-hateoas/src/main/java/com/example/notes/NotePatchInput.java @@ -1,5 +1,5 @@ /* - * Copyright 2014 the original author or authors. + * Copyright 2014-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,7 +23,7 @@ import java.util.List; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; -public class NotePatchInput { +class NotePatchInput { @NullOrNotBlank private final String title; @@ -33,23 +33,23 @@ public class NotePatchInput { private final List tagUris; @JsonCreator - public NotePatchInput(@JsonProperty("title") String title, + NotePatchInput(@JsonProperty("title") String title, @JsonProperty("body") String body, @JsonProperty("tags") List tagUris) { this.title = title; this.body = body; this.tagUris = tagUris == null ? Collections.emptyList() : tagUris; } - public String getTitle() { + String getTitle() { return title; } - public String getBody() { + String getBody() { return body; } @JsonProperty("tags") - public List getTagUris() { + List getTagUris() { return this.tagUris; } } diff --git a/samples/rest-notes-spring-hateoas/src/main/java/com/example/notes/NoteRepository.java b/samples/rest-notes-spring-hateoas/src/main/java/com/example/notes/NoteRepository.java index ac781323..cd9e4735 100644 --- a/samples/rest-notes-spring-hateoas/src/main/java/com/example/notes/NoteRepository.java +++ b/samples/rest-notes-spring-hateoas/src/main/java/com/example/notes/NoteRepository.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2015 the original author or authors. + * Copyright 2014-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,9 +21,10 @@ import java.util.List; import org.springframework.data.repository.CrudRepository; -public interface NoteRepository extends CrudRepository { +interface NoteRepository extends CrudRepository { Note findById(long id); List findByTagsIn(Collection tags); + } diff --git a/samples/rest-notes-spring-hateoas/src/main/java/com/example/notes/NoteRepresentationModelAssembler.java b/samples/rest-notes-spring-hateoas/src/main/java/com/example/notes/NoteRepresentationModelAssembler.java new file mode 100644 index 00000000..eb9e4ab6 --- /dev/null +++ b/samples/rest-notes-spring-hateoas/src/main/java/com/example/notes/NoteRepresentationModelAssembler.java @@ -0,0 +1,67 @@ +/* + * Copyright 2014-2021 the original author or authors. + * + * Licensed 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 + * + * https://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. + */ + +package com.example.notes; + +import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.linkTo; +import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.methodOn; + +import org.springframework.hateoas.RepresentationModel; +import org.springframework.hateoas.server.core.Relation; +import org.springframework.hateoas.server.mvc.RepresentationModelAssemblerSupport; +import org.springframework.stereotype.Component; + +import com.example.notes.NoteRepresentationModelAssembler.NoteModel; + +@Component +class NoteRepresentationModelAssembler extends RepresentationModelAssemblerSupport { + + NoteRepresentationModelAssembler() { + super(NotesController.class, NoteModel.class); + } + + @Override + public NoteModel toModel(Note entity) { + NoteModel noteModel = createModelWithId(entity.getId(), entity); + noteModel.add(linkTo(methodOn(NotesController.class).noteTags(entity.getId())).withRel("note-tags")); + return noteModel; + } + + @Override + protected NoteModel instantiateModel(Note entity) { + return new NoteModel(entity); + } + + @Relation(collectionRelation = "notes", itemRelation = "note") + static class NoteModel extends RepresentationModel { + + private final Note note; + + NoteModel(Note note) { + this.note = note; + } + + public String getTitle() { + return this.note.getTitle(); + } + + public String getBody() { + return this.note.getBody(); + } + + } + +} diff --git a/samples/rest-notes-spring-hateoas/src/main/java/com/example/notes/NoteResourceAssembler.java b/samples/rest-notes-spring-hateoas/src/main/java/com/example/notes/NoteResourceAssembler.java deleted file mode 100644 index 4a7f5108..00000000 --- a/samples/rest-notes-spring-hateoas/src/main/java/com/example/notes/NoteResourceAssembler.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright 2014 the original author or authors. - * - * Licensed 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 - * - * https://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. - */ - -package com.example.notes; - -import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo; - -import org.springframework.hateoas.Resource; -import org.springframework.hateoas.mvc.ResourceAssemblerSupport; -import org.springframework.stereotype.Component; - -import com.example.notes.NoteResourceAssembler.NoteResource; - -@Component -public class NoteResourceAssembler extends ResourceAssemblerSupport { - - public NoteResourceAssembler() { - super(NotesController.class, NoteResource.class); - } - - @Override - public NoteResource toResource(Note note) { - NoteResource resource = createResourceWithId(note.getId(), note); - resource.add(linkTo(NotesController.class).slash(note.getId()).slash("tags") - .withRel("note-tags")); - return resource; - } - - @Override - protected NoteResource instantiateResource(Note entity) { - return new NoteResource(entity); - } - - static class NoteResource extends Resource { - - public NoteResource(Note content) { - super(content); - } - } - -} diff --git a/samples/rest-notes-spring-hateoas/src/main/java/com/example/notes/NotesController.java b/samples/rest-notes-spring-hateoas/src/main/java/com/example/notes/NotesController.java index 3460d88a..ab721849 100644 --- a/samples/rest-notes-spring-hateoas/src/main/java/com/example/notes/NotesController.java +++ b/samples/rest-notes-spring-hateoas/src/main/java/com/example/notes/NotesController.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2017 the original author or authors. + * Copyright 2014-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,15 +16,13 @@ package com.example.notes; -import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo; +import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.linkTo; import java.net.URI; import java.util.ArrayList; import java.util.List; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.hateoas.Resource; -import org.springframework.hateoas.ResourceSupport; +import org.springframework.hateoas.CollectionModel; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.PathVariable; @@ -35,37 +33,34 @@ import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.util.UriTemplate; -import com.example.notes.NoteResourceAssembler.NoteResource; -import com.example.notes.TagResourceAssembler.TagResource; +import com.example.notes.NoteRepresentationModelAssembler.NoteModel; +import com.example.notes.TagRepresentationModelAssembler.TagModel; @RestController @RequestMapping("/notes") -public class NotesController { +class NotesController { private static final UriTemplate TAG_URI_TEMPLATE = new UriTemplate("/tags/{id}"); private final NoteRepository noteRepository; private final TagRepository tagRepository; + + private final NoteRepresentationModelAssembler noteAssembler; + + private final TagRepresentationModelAssembler tagAssembler; - private final NoteResourceAssembler noteResourceAssembler; - - private final TagResourceAssembler tagResourceAssembler; - - @Autowired - public NotesController(NoteRepository noteRepository, TagRepository tagRepository, - NoteResourceAssembler noteResourceAssembler, - TagResourceAssembler tagResourceAssembler) { + NotesController(NoteRepository noteRepository, TagRepository tagRepository, + NoteRepresentationModelAssembler noteAssembler, TagRepresentationModelAssembler tagAssembler) { this.noteRepository = noteRepository; this.tagRepository = tagRepository; - this.noteResourceAssembler = noteResourceAssembler; - this.tagResourceAssembler = tagResourceAssembler; + this.noteAssembler = noteAssembler; + this.tagAssembler = tagAssembler; } @RequestMapping(method = RequestMethod.GET) - NestedContentResource all() { - return new NestedContentResource( - this.noteResourceAssembler.toResources(this.noteRepository.findAll())); + CollectionModel all() { + return noteAssembler.toCollectionModel(this.noteRepository.findAll()); } @ResponseStatus(HttpStatus.CREATED) @@ -91,14 +86,13 @@ public class NotesController { } @RequestMapping(value = "/{id}", method = RequestMethod.GET) - Resource note(@PathVariable("id") long id) { - return this.noteResourceAssembler.toResource(findNoteById(id)); + NoteModel note(@PathVariable("id") long id) { + return this.noteAssembler.toModel(findNoteById(id)); } @RequestMapping(value = "/{id}/tags", method = RequestMethod.GET) - ResourceSupport noteTags(@PathVariable("id") long id) { - return new NestedContentResource( - this.tagResourceAssembler.toResources(findNoteById(id).getTags())); + CollectionModel noteTags(@PathVariable("id") long id) { + return this.tagAssembler.toCollectionModel(findNoteById(id).getTags()); } @RequestMapping(value = "/{id}", method = RequestMethod.PATCH) diff --git a/samples/rest-notes-spring-hateoas/src/main/java/com/example/notes/NullOrNotBlank.java b/samples/rest-notes-spring-hateoas/src/main/java/com/example/notes/NullOrNotBlank.java index 70ace33f..23dbbd45 100644 --- a/samples/rest-notes-spring-hateoas/src/main/java/com/example/notes/NullOrNotBlank.java +++ b/samples/rest-notes-spring-hateoas/src/main/java/com/example/notes/NullOrNotBlank.java @@ -1,5 +1,5 @@ /* - * Copyright 2014 the original author or authors. + * Copyright 2014-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -35,7 +35,7 @@ import org.hibernate.validator.constraints.ConstraintComposition; @NotBlank @Target({ ElementType.FIELD, ElementType.PARAMETER }) @Retention(RetentionPolicy.RUNTIME) -public @interface NullOrNotBlank { +@interface NullOrNotBlank { String message() default "Must be null or not blank"; diff --git a/samples/rest-notes-spring-hateoas/src/main/java/com/example/notes/ResourceDoesNotExistException.java b/samples/rest-notes-spring-hateoas/src/main/java/com/example/notes/ResourceDoesNotExistException.java index 03e23e74..88327a6f 100644 --- a/samples/rest-notes-spring-hateoas/src/main/java/com/example/notes/ResourceDoesNotExistException.java +++ b/samples/rest-notes-spring-hateoas/src/main/java/com/example/notes/ResourceDoesNotExistException.java @@ -1,5 +1,5 @@ /* - * Copyright 2014 the original author or authors. + * Copyright 2014-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,6 +17,6 @@ package com.example.notes; @SuppressWarnings("serial") -public class ResourceDoesNotExistException extends RuntimeException { +class ResourceDoesNotExistException extends RuntimeException { } diff --git a/samples/rest-notes-spring-hateoas/src/main/java/com/example/notes/RestNotesControllerAdvice.java b/samples/rest-notes-spring-hateoas/src/main/java/com/example/notes/RestNotesControllerAdvice.java index e621709e..bef998e2 100644 --- a/samples/rest-notes-spring-hateoas/src/main/java/com/example/notes/RestNotesControllerAdvice.java +++ b/samples/rest-notes-spring-hateoas/src/main/java/com/example/notes/RestNotesControllerAdvice.java @@ -1,5 +1,5 @@ /* - * Copyright 2014 the original author or authors. + * Copyright 2014-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,16 +26,16 @@ import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; @ControllerAdvice -public class RestNotesControllerAdvice { +class RestNotesControllerAdvice { @ExceptionHandler(IllegalArgumentException.class) - public void handleIllegalArgumentException(IllegalArgumentException ex, + void handleIllegalArgumentException(IllegalArgumentException ex, HttpServletResponse response) throws IOException { response.sendError(HttpStatus.BAD_REQUEST.value(), ex.getMessage()); } @ExceptionHandler(ResourceDoesNotExistException.class) - public void handleResourceDoesNotExistException(ResourceDoesNotExistException ex, + void handleResourceDoesNotExistException(ResourceDoesNotExistException ex, HttpServletRequest request, HttpServletResponse response) throws IOException { response.sendError(HttpStatus.NOT_FOUND.value(), "The resource '" + request.getRequestURI() + "' does not exist"); diff --git a/samples/rest-notes-spring-hateoas/src/main/java/com/example/notes/RestNotesSpringHateoas.java b/samples/rest-notes-spring-hateoas/src/main/java/com/example/notes/RestNotesSpringHateoas.java index f0814465..f2916a12 100644 --- a/samples/rest-notes-spring-hateoas/src/main/java/com/example/notes/RestNotesSpringHateoas.java +++ b/samples/rest-notes-spring-hateoas/src/main/java/com/example/notes/RestNotesSpringHateoas.java @@ -20,7 +20,7 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication -public class RestNotesSpringHateoas { +class RestNotesSpringHateoas { public static void main(String[] args) { SpringApplication.run(RestNotesSpringHateoas.class, args); diff --git a/samples/rest-notes-spring-hateoas/src/main/java/com/example/notes/Tag.java b/samples/rest-notes-spring-hateoas/src/main/java/com/example/notes/Tag.java index f721fcb5..b8274227 100644 --- a/samples/rest-notes-spring-hateoas/src/main/java/com/example/notes/Tag.java +++ b/samples/rest-notes-spring-hateoas/src/main/java/com/example/notes/Tag.java @@ -24,8 +24,6 @@ import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.ManyToMany; -import com.fasterxml.jackson.annotation.JsonIgnore; - @Entity public class Tag { @@ -38,7 +36,6 @@ public class Tag { @ManyToMany(mappedBy = "tags") private List notes; - @JsonIgnore public long getId() { return id; } @@ -55,7 +52,6 @@ public class Tag { this.name = name; } - @JsonIgnore public List getNotes() { return notes; } @@ -63,4 +59,5 @@ public class Tag { public void setNotes(List notes) { this.notes = notes; } + } diff --git a/samples/rest-notes-spring-hateoas/src/main/java/com/example/notes/TagInput.java b/samples/rest-notes-spring-hateoas/src/main/java/com/example/notes/TagInput.java index 08fa5286..bc9b397a 100644 --- a/samples/rest-notes-spring-hateoas/src/main/java/com/example/notes/TagInput.java +++ b/samples/rest-notes-spring-hateoas/src/main/java/com/example/notes/TagInput.java @@ -1,5 +1,5 @@ /* - * Copyright 2014 the original author or authors. + * Copyright 2014-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,17 +21,17 @@ import javax.validation.constraints.NotBlank; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; -public class TagInput { +class TagInput { @NotBlank private final String name; @JsonCreator - public TagInput(@NotBlank @JsonProperty("name") String name) { + TagInput(@NotBlank @JsonProperty("name") String name) { this.name = name; } - public String getName() { + String getName() { return name; } diff --git a/samples/rest-notes-spring-hateoas/src/main/java/com/example/notes/TagPatchInput.java b/samples/rest-notes-spring-hateoas/src/main/java/com/example/notes/TagPatchInput.java index b392492a..fd9e8c2f 100644 --- a/samples/rest-notes-spring-hateoas/src/main/java/com/example/notes/TagPatchInput.java +++ b/samples/rest-notes-spring-hateoas/src/main/java/com/example/notes/TagPatchInput.java @@ -1,5 +1,5 @@ /* - * Copyright 2014 the original author or authors. + * Copyright 2014-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,17 +19,17 @@ package com.example.notes; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; -public class TagPatchInput { +class TagPatchInput { @NullOrNotBlank private final String name; @JsonCreator - public TagPatchInput(@NullOrNotBlank @JsonProperty("name") String name) { + TagPatchInput(@NullOrNotBlank @JsonProperty("name") String name) { this.name = name; } - public String getName() { + String getName() { return name; } diff --git a/samples/rest-notes-spring-hateoas/src/main/java/com/example/notes/TagRepository.java b/samples/rest-notes-spring-hateoas/src/main/java/com/example/notes/TagRepository.java index 6bc9fc3c..f9206526 100644 --- a/samples/rest-notes-spring-hateoas/src/main/java/com/example/notes/TagRepository.java +++ b/samples/rest-notes-spring-hateoas/src/main/java/com/example/notes/TagRepository.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2015 the original author or authors. + * Copyright 2014-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,7 +18,7 @@ package com.example.notes; import org.springframework.data.repository.CrudRepository; -public interface TagRepository extends CrudRepository { +interface TagRepository extends CrudRepository { Tag findById(long id); diff --git a/samples/rest-notes-spring-hateoas/src/main/java/com/example/notes/TagRepresentationModelAssembler.java b/samples/rest-notes-spring-hateoas/src/main/java/com/example/notes/TagRepresentationModelAssembler.java new file mode 100644 index 00000000..e2b4a59b --- /dev/null +++ b/samples/rest-notes-spring-hateoas/src/main/java/com/example/notes/TagRepresentationModelAssembler.java @@ -0,0 +1,64 @@ +/* + * Copyright 2014-2021 the original author or authors. + * + * Licensed 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 + * + * https://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. + */ + +package com.example.notes; + +import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.linkTo; +import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.methodOn; + +import org.springframework.hateoas.RepresentationModel; +import org.springframework.hateoas.server.core.Relation; +import org.springframework.hateoas.server.mvc.RepresentationModelAssemblerSupport; +import org.springframework.stereotype.Component; + +import com.example.notes.TagRepresentationModelAssembler.TagModel; + +@Component +class TagRepresentationModelAssembler extends RepresentationModelAssemblerSupport { + + TagRepresentationModelAssembler() { + super(TagsController.class, TagModel.class); + } + + @Override + public TagModel toModel(Tag entity) { + TagModel model = new TagModel(entity); + model.add(linkTo(methodOn(TagsController.class).tag(entity.getId())).withSelfRel(), + linkTo(methodOn(TagsController.class).tagNotes(entity.getId())).withRel("tagged-notes")); + return model; + } + + @Override + protected TagModel instantiateModel(Tag entity) { + return new TagModel(entity); + } + + @Relation(collectionRelation = "tags", itemRelation = "tag") + static class TagModel extends RepresentationModel { + + private final Tag tag; + + TagModel(Tag tag) { + this.tag = tag; + } + + public String getName() { + return this.tag.getName(); + } + + } + +} diff --git a/samples/rest-notes-spring-hateoas/src/main/java/com/example/notes/TagResourceAssembler.java b/samples/rest-notes-spring-hateoas/src/main/java/com/example/notes/TagResourceAssembler.java deleted file mode 100644 index 45d88179..00000000 --- a/samples/rest-notes-spring-hateoas/src/main/java/com/example/notes/TagResourceAssembler.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright 2014 the original author or authors. - * - * Licensed 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 - * - * https://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. - */ - -package com.example.notes; - -import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo; - -import org.springframework.hateoas.Resource; -import org.springframework.hateoas.mvc.ResourceAssemblerSupport; -import org.springframework.stereotype.Component; - -import com.example.notes.TagResourceAssembler.TagResource; - -@Component -public class TagResourceAssembler extends ResourceAssemblerSupport { - - public TagResourceAssembler() { - super(TagsController.class, TagResource.class); - } - - @Override - public TagResource toResource(Tag tag) { - TagResource resource = createResourceWithId(tag.getId(), tag); - resource.add(linkTo(TagsController.class).slash(tag.getId()).slash("notes") - .withRel("tagged-notes")); - return resource; - } - - @Override - protected TagResource instantiateResource(Tag entity) { - return new TagResource(entity); - } - - static class TagResource extends Resource { - - public TagResource(Tag content) { - super(content); - } - } - -} diff --git a/samples/rest-notes-spring-hateoas/src/main/java/com/example/notes/TagsController.java b/samples/rest-notes-spring-hateoas/src/main/java/com/example/notes/TagsController.java index 461aef04..945cc17b 100644 --- a/samples/rest-notes-spring-hateoas/src/main/java/com/example/notes/TagsController.java +++ b/samples/rest-notes-spring-hateoas/src/main/java/com/example/notes/TagsController.java @@ -16,11 +16,9 @@ package com.example.notes; -import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo; +import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.linkTo; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.hateoas.Resource; -import org.springframework.hateoas.ResourceSupport; +import org.springframework.hateoas.CollectionModel; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.PathVariable; @@ -30,32 +28,29 @@ import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestController; -import com.example.notes.NoteResourceAssembler.NoteResource; -import com.example.notes.TagResourceAssembler.TagResource; +import com.example.notes.NoteRepresentationModelAssembler.NoteModel; +import com.example.notes.TagRepresentationModelAssembler.TagModel; @RestController @RequestMapping("tags") -public class TagsController { +class TagsController { private final TagRepository repository; + + private final TagRepresentationModelAssembler tagAssembler; + + private final NoteRepresentationModelAssembler noteAssembler; - private final NoteResourceAssembler noteResourceAssembler; - - private final TagResourceAssembler tagResourceAssembler; - - @Autowired - public TagsController(TagRepository repository, - NoteResourceAssembler noteResourceAssembler, - TagResourceAssembler tagResourceAssembler) { + TagsController(TagRepository repository, TagRepresentationModelAssembler tagAssembler, + NoteRepresentationModelAssembler noteAssembler) { this.repository = repository; - this.noteResourceAssembler = noteResourceAssembler; - this.tagResourceAssembler = tagResourceAssembler; + this.tagAssembler = tagAssembler; + this.noteAssembler = noteAssembler; } @RequestMapping(method = RequestMethod.GET) - NestedContentResource all() { - return new NestedContentResource( - this.tagResourceAssembler.toResources(this.repository.findAll())); + CollectionModel all() { + return this.tagAssembler.toCollectionModel(this.repository.findAll()); } @ResponseStatus(HttpStatus.CREATED) @@ -78,16 +73,13 @@ public class TagsController { } @RequestMapping(value = "/{id}", method = RequestMethod.GET) - Resource tag(@PathVariable("id") long id) { - Tag tag = findTagById(id); - return this.tagResourceAssembler.toResource(tag); + TagModel tag(@PathVariable("id") long id) { + return this.tagAssembler.toModel(findTagById(id)); } @RequestMapping(value = "/{id}/notes", method = RequestMethod.GET) - ResourceSupport tagNotes(@PathVariable("id") long id) { - Tag tag = findTagById(id); - return new NestedContentResource( - this.noteResourceAssembler.toResources(tag.getNotes())); + CollectionModel tagNotes(@PathVariable("id") long id) { + return this.noteAssembler.toCollectionModel(findTagById(id).getNotes()); } private Tag findTagById(long id) { diff --git a/samples/testng/build.gradle b/samples/testng/build.gradle index f2f16497..f59974a0 100644 --- a/samples/testng/build.gradle +++ b/samples/testng/build.gradle @@ -1,19 +1,10 @@ -buildscript { - repositories { - mavenCentral() - } - dependencies { - classpath 'org.springframework.boot:spring-boot-gradle-plugin:2.1.9.RELEASE' - } -} - plugins { + id "eclipse" + id "java" id "org.asciidoctor.jvm.convert" version "3.3.2" + id "org.springframework.boot" version "2.4.7" } -apply plugin: 'java' -apply plugin: 'org.springframework.boot' -apply plugin: 'eclipse' apply plugin: 'io.spring.dependency-management' repositories { @@ -41,9 +32,7 @@ configurations { dependencies { asciidoctorExtensions 'org.springframework.restdocs:spring-restdocs-asciidoctor' implementation 'org.springframework.boot:spring-boot-starter-web' - testImplementation('org.springframework.boot:spring-boot-starter-test') { - exclude group: 'junit', module: 'junit;' - } + testImplementation 'org.springframework.boot:spring-boot-starter-test' testImplementation 'org.springframework.restdocs:spring-restdocs-mockmvc' testImplementation 'org.testng:testng:6.9.10' }