Improve code structure, javadoc, and build configuration
This commit makes extensive changes to the structure of the code. It introduces a number of separate packages to provide better separation of the various areas of functionality. Alongside this change the project has been renamed from spring-restdocs-core to spring-restdocs. The build has been improved to provide support for building the samples from the main build using the buildSamples task. While this change has been made, the samples remain standalone projects so that their configuration is not dependent on the main project’s build. Running buildSamples will build the samples using both Maven and Gradle. All of the main project’s classes now have javadoc and licence/copyright headers.
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package com.example.notes;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
abstract class AbstractNoteInput {
|
||||
|
||||
private final String title;
|
||||
|
||||
private final String body;
|
||||
|
||||
private final List<URI> tagUris;
|
||||
|
||||
public AbstractNoteInput(String title, String body, List<URI> tagUris) {
|
||||
this.title = title;
|
||||
this.body = body;
|
||||
this.tagUris = tagUris == null ? Collections.<URI> emptyList() : tagUris;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public String getBody() {
|
||||
return body;
|
||||
}
|
||||
|
||||
@JsonProperty("tags")
|
||||
public List<URI> getTagUris() {
|
||||
return this.tagUris;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package com.example.notes;
|
||||
|
||||
|
||||
abstract class AbstractTagInput {
|
||||
|
||||
private final String name;
|
||||
|
||||
public AbstractTagInput(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package com.example.notes;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.boot.autoconfigure.web.DefaultErrorAttributes;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.context.request.RequestAttributes;
|
||||
|
||||
@Component
|
||||
class ExceptionSupressingErrorAttributes extends DefaultErrorAttributes {
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getErrorAttributes(RequestAttributes requestAttributes,
|
||||
boolean includeStackTrace) {
|
||||
Map<String, Object> errorAttributes = super.getErrorAttributes(requestAttributes, includeStackTrace);
|
||||
errorAttributes.remove("exception");
|
||||
Object message = requestAttributes.getAttribute("javax.servlet.error.message", RequestAttributes.SCOPE_REQUEST);
|
||||
if (message != null) {
|
||||
errorAttributes.put("message", message);
|
||||
}
|
||||
return errorAttributes;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package com.example.notes;
|
||||
|
||||
import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo;
|
||||
|
||||
import org.springframework.hateoas.ResourceSupport;
|
||||
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 {
|
||||
|
||||
@RequestMapping(method=RequestMethod.GET)
|
||||
public ResourceSupport index() {
|
||||
ResourceSupport index = new ResourceSupport();
|
||||
index.add(linkTo(NotesController.class).withRel("notes"));
|
||||
index.add(linkTo(TagsController.class).withRel("tags"));
|
||||
return index;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package com.example.notes;
|
||||
|
||||
import org.springframework.hateoas.ResourceSupport;
|
||||
import org.springframework.hateoas.Resources;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonUnwrapped;
|
||||
|
||||
public class NestedContentResource<T> extends ResourceSupport {
|
||||
|
||||
private final Resources<T> nested;
|
||||
|
||||
public NestedContentResource(Iterable<T> toNest) {
|
||||
this.nested = new Resources<T>(toNest);
|
||||
}
|
||||
|
||||
@JsonUnwrapped
|
||||
public Resources<T> getNested() {
|
||||
return this.nested;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package com.example.notes;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.GenerationType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.ManyToMany;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
|
||||
@Entity
|
||||
public class Note {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.AUTO)
|
||||
private long id;
|
||||
|
||||
private String title;
|
||||
|
||||
private String body;
|
||||
|
||||
@ManyToMany
|
||||
private List<Tag> tags;
|
||||
|
||||
@JsonIgnore
|
||||
public long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public void setTitle(String title) {
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
public String getBody() {
|
||||
return body;
|
||||
}
|
||||
|
||||
public void setBody(String body) {
|
||||
this.body = body;
|
||||
}
|
||||
|
||||
@JsonIgnore
|
||||
public List<Tag> getTags() {
|
||||
return tags;
|
||||
}
|
||||
|
||||
public void setTags(List<Tag> tags) {
|
||||
this.tags = tags;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package com.example.notes;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.List;
|
||||
|
||||
import org.hibernate.validator.constraints.NotBlank;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
public class NoteInput extends AbstractNoteInput {
|
||||
|
||||
@JsonCreator
|
||||
public NoteInput(@NotBlank @JsonProperty("title") String title,
|
||||
@JsonProperty("body") String body, @JsonProperty("tags") List<URI> tagUris) {
|
||||
super(title, body, tagUris);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package com.example.notes;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.List;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
public class NotePatchInput extends AbstractNoteInput {
|
||||
|
||||
@JsonCreator
|
||||
public NotePatchInput(@NullOrNotBlank @JsonProperty("title") String title,
|
||||
@JsonProperty("body") String body, @JsonProperty("tags") List<URI> tagUris) {
|
||||
super(title, body, tagUris);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package com.example.notes;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.data.repository.CrudRepository;
|
||||
|
||||
public interface NoteRepository extends CrudRepository<Note, Long> {
|
||||
|
||||
Optional<Note> findById(long id);
|
||||
|
||||
List<Note> findByTagsIn(Collection<Tag> tags);
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
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<Note, NoteResource> {
|
||||
|
||||
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<Note> {
|
||||
|
||||
public NoteResource(Note content) {
|
||||
super(content);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package com.example.notes;
|
||||
|
||||
import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.hateoas.Resource;
|
||||
import org.springframework.hateoas.ResourceSupport;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
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;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/notes")
|
||||
public class NotesController {
|
||||
|
||||
private static final UriTemplate TAG_URI_TEMPLATE = new UriTemplate("/tags/{id}");
|
||||
|
||||
private final NoteRepository noteRepository;
|
||||
|
||||
private final TagRepository tagRepository;
|
||||
|
||||
private final NoteResourceAssembler noteResourceAssembler;
|
||||
|
||||
private final TagResourceAssembler tagResourceAssembler;
|
||||
|
||||
@Autowired
|
||||
public NotesController(NoteRepository noteRepository, TagRepository tagRepository,
|
||||
NoteResourceAssembler noteResourceAssembler,
|
||||
TagResourceAssembler tagResourceAssembler) {
|
||||
this.noteRepository = noteRepository;
|
||||
this.tagRepository = tagRepository;
|
||||
this.noteResourceAssembler = noteResourceAssembler;
|
||||
this.tagResourceAssembler = tagResourceAssembler;
|
||||
}
|
||||
|
||||
@RequestMapping(method = RequestMethod.GET)
|
||||
NestedContentResource<NoteResource> all() {
|
||||
return new NestedContentResource<NoteResource>(
|
||||
this.noteResourceAssembler.toResources(this.noteRepository.findAll()));
|
||||
}
|
||||
|
||||
@ResponseStatus(HttpStatus.CREATED)
|
||||
@RequestMapping(method = RequestMethod.POST)
|
||||
HttpHeaders create(@RequestBody NoteInput noteInput) {
|
||||
Note note = new Note();
|
||||
note.setTitle(noteInput.getTitle());
|
||||
note.setBody(noteInput.getBody());
|
||||
note.setTags(getTags(noteInput.getTagUris()));
|
||||
|
||||
this.noteRepository.save(note);
|
||||
|
||||
HttpHeaders httpHeaders = new HttpHeaders();
|
||||
httpHeaders
|
||||
.setLocation(linkTo(NotesController.class).slash(note.getId()).toUri());
|
||||
|
||||
return httpHeaders;
|
||||
}
|
||||
|
||||
@RequestMapping(value = "/{id}", method = RequestMethod.DELETE)
|
||||
void delete(@PathVariable("id") long id) {
|
||||
this.noteRepository.delete(id);
|
||||
}
|
||||
|
||||
@RequestMapping(value = "/{id}", method = RequestMethod.GET)
|
||||
Resource<Note> note(@PathVariable("id") long id) {
|
||||
Note note = this.noteRepository.findById(id).orElseThrow(
|
||||
() -> new ResourceDoesNotExistException());
|
||||
return this.noteResourceAssembler.toResource(note);
|
||||
}
|
||||
|
||||
@RequestMapping(value = "/{id}/tags", method = RequestMethod.GET)
|
||||
ResourceSupport noteTags(@PathVariable("id") long id) {
|
||||
return new NestedContentResource<TagResource>(
|
||||
this.tagResourceAssembler.toResources(this.noteRepository
|
||||
.findById(id)
|
||||
.orElseThrow(
|
||||
() -> new ResourceDoesNotExistException()).getTags()));
|
||||
}
|
||||
|
||||
@RequestMapping(value = "/{id}", method = RequestMethod.PATCH)
|
||||
@ResponseStatus(HttpStatus.NO_CONTENT)
|
||||
void updateNote(@PathVariable("id") long id, @RequestBody NotePatchInput noteInput) {
|
||||
Note note = this.noteRepository.findById(id).orElseThrow(
|
||||
() -> new ResourceDoesNotExistException());
|
||||
if (noteInput.getTagUris() != null) {
|
||||
note.setTags(getTags(noteInput.getTagUris()));
|
||||
}
|
||||
if (noteInput.getTitle() != null) {
|
||||
note.setTitle(noteInput.getTitle());
|
||||
}
|
||||
if (noteInput.getBody() != null) {
|
||||
note.setBody(noteInput.getBody());
|
||||
}
|
||||
this.noteRepository.save(note);
|
||||
}
|
||||
|
||||
private List<Tag> getTags(List<URI> tagLocations) {
|
||||
return tagLocations
|
||||
.stream()
|
||||
.map(location -> this.tagRepository.findById(extractTagId(location))
|
||||
.<IllegalArgumentException> orElseThrow(
|
||||
() -> new IllegalArgumentException("The tag '" + location
|
||||
+ "' does not exist")))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private long extractTagId(URI tagLocation) {
|
||||
try {
|
||||
String idString = TAG_URI_TEMPLATE.match(tagLocation.toASCIIString()).get(
|
||||
"id");
|
||||
return Long.valueOf(idString);
|
||||
}
|
||||
catch (RuntimeException ex) {
|
||||
throw new IllegalArgumentException("The tag '" + tagLocation + "' is invalid");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package com.example.notes;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
import org.hibernate.validator.constraints.CompositionType;
|
||||
import org.hibernate.validator.constraints.ConstraintComposition;
|
||||
import org.hibernate.validator.constraints.NotBlank;
|
||||
|
||||
@ConstraintComposition(CompositionType.OR)
|
||||
@NotNull
|
||||
@NotBlank
|
||||
@Target({ ElementType.FIELD, ElementType.PARAMETER })
|
||||
public @interface NullOrNotBlank {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package com.example.notes;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public class ResourceDoesNotExistException extends RuntimeException {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package com.example.notes;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.web.bind.annotation.ControllerAdvice;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
|
||||
@ControllerAdvice
|
||||
public class RestNotesControllerAdvice {
|
||||
|
||||
@ExceptionHandler(IllegalArgumentException.class)
|
||||
public void handleIllegalArgumentException(IllegalArgumentException ex,
|
||||
HttpServletResponse response) throws IOException {
|
||||
response.sendError(HttpStatus.BAD_REQUEST.value(), ex.getMessage());
|
||||
}
|
||||
|
||||
@ExceptionHandler(ResourceDoesNotExistException.class)
|
||||
public void handleResourceDoesNotExistException(ResourceDoesNotExistException ex,
|
||||
HttpServletRequest request, HttpServletResponse response) throws IOException {
|
||||
response.sendError(HttpStatus.NOT_FOUND.value(),
|
||||
"The resource '" + request.getRequestURI() + "' does not exist");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package com.example.notes;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
|
||||
|
||||
@EnableAutoConfiguration
|
||||
@ComponentScan
|
||||
@EnableJpaRepositories
|
||||
public class RestNotesSpringHateoas {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(RestNotesSpringHateoas.class, args);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package com.example.notes;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.GenerationType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.ManyToMany;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
|
||||
@Entity
|
||||
public class Tag {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.AUTO)
|
||||
private long id;
|
||||
|
||||
private String name;
|
||||
|
||||
@ManyToMany(mappedBy = "tags")
|
||||
private List<Note> notes;
|
||||
|
||||
@JsonIgnore
|
||||
public long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@JsonIgnore
|
||||
public List<Note> getNotes() {
|
||||
return notes;
|
||||
}
|
||||
|
||||
public void setNotes(List<Note> notes) {
|
||||
this.notes = notes;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package com.example.notes;
|
||||
|
||||
import org.hibernate.validator.constraints.NotBlank;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
public class TagInput extends AbstractTagInput {
|
||||
|
||||
@JsonCreator
|
||||
public TagInput(@NotBlank @JsonProperty("name") String name) {
|
||||
super(name);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package com.example.notes;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
public class TagPatchInput extends AbstractTagInput {
|
||||
|
||||
@JsonCreator
|
||||
public TagPatchInput(@NullOrNotBlank @JsonProperty("name") String name) {
|
||||
super(name);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package com.example.notes;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.data.repository.CrudRepository;
|
||||
|
||||
public interface TagRepository extends CrudRepository<Tag, Long> {
|
||||
|
||||
Optional<Tag> findById(long id);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
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<Tag, TagResource> {
|
||||
|
||||
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<Tag> {
|
||||
|
||||
public TagResource(Tag content) {
|
||||
super(content);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package com.example.notes;
|
||||
|
||||
import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.hateoas.Resource;
|
||||
import org.springframework.hateoas.ResourceSupport;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
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;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("tags")
|
||||
public class TagsController {
|
||||
|
||||
private final TagRepository repository;
|
||||
|
||||
private final NoteResourceAssembler noteResourceAssembler;
|
||||
|
||||
private final TagResourceAssembler tagResourceAssembler;
|
||||
|
||||
@Autowired
|
||||
public TagsController(TagRepository repository,
|
||||
NoteResourceAssembler noteResourceAssembler,
|
||||
TagResourceAssembler tagResourceAssembler) {
|
||||
this.repository = repository;
|
||||
this.noteResourceAssembler = noteResourceAssembler;
|
||||
this.tagResourceAssembler = tagResourceAssembler;
|
||||
}
|
||||
|
||||
@RequestMapping(method = RequestMethod.GET)
|
||||
NestedContentResource<TagResource> all() {
|
||||
return new NestedContentResource<TagResource>(
|
||||
this.tagResourceAssembler.toResources(this.repository.findAll()));
|
||||
}
|
||||
|
||||
@ResponseStatus(HttpStatus.CREATED)
|
||||
@RequestMapping(method = RequestMethod.POST)
|
||||
HttpHeaders create(@RequestBody TagInput tagInput) {
|
||||
Tag tag = new Tag();
|
||||
tag.setName(tagInput.getName());
|
||||
|
||||
this.repository.save(tag);
|
||||
|
||||
HttpHeaders httpHeaders = new HttpHeaders();
|
||||
httpHeaders.setLocation(linkTo(TagsController.class).slash(tag.getId()).toUri());
|
||||
|
||||
return httpHeaders;
|
||||
}
|
||||
|
||||
@RequestMapping(value = "/{id}", method = RequestMethod.DELETE)
|
||||
void delete(@PathVariable("id") long id) {
|
||||
this.repository.delete(id);
|
||||
}
|
||||
|
||||
@RequestMapping(value = "/{id}", method = RequestMethod.GET)
|
||||
Resource<Tag> tag(@PathVariable("id") long id) {
|
||||
Tag tag = this.repository.findById(id).orElseThrow(
|
||||
() -> new ResourceDoesNotExistException());
|
||||
return this.tagResourceAssembler.toResource(tag);
|
||||
}
|
||||
|
||||
@RequestMapping(value = "/{id}/notes", method = RequestMethod.GET)
|
||||
ResourceSupport tagNotes(@PathVariable("id") long id) {
|
||||
return new NestedContentResource<NoteResource>(
|
||||
this.noteResourceAssembler.toResources(this.repository.findById(id)
|
||||
.orElseThrow(() -> new ResourceDoesNotExistException())
|
||||
.getNotes()));
|
||||
}
|
||||
|
||||
@RequestMapping(value = "/{id}", method = RequestMethod.PATCH)
|
||||
@ResponseStatus(HttpStatus.NO_CONTENT)
|
||||
void updateTag(@PathVariable("id") long id, @RequestBody TagPatchInput tagInput) {
|
||||
Tag tag = this.repository.findById(id).orElseThrow(
|
||||
() -> new ResourceDoesNotExistException());
|
||||
if (tagInput.getName() != null) {
|
||||
tag.setName(tagInput.getName());
|
||||
}
|
||||
this.repository.save(tag);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user