Initial commit
This commit is contained in:
@@ -0,0 +1,173 @@
|
||||
= RESTful Notes User Guide
|
||||
Andy Wilkinson;
|
||||
:doctype: book
|
||||
:toc:
|
||||
:toclevels: 4
|
||||
:source-highlighter: prettify
|
||||
|
||||
[introduction]
|
||||
= Introduction
|
||||
|
||||
RESTful Notes is a RESTful web service for creating and storing notes. It uses hypermedia
|
||||
to describe the relationships between resources and to allow navigation between them.
|
||||
|
||||
[getting-started]
|
||||
= Getting started
|
||||
|
||||
|
||||
|
||||
[getting-started-running-the-service]
|
||||
== Running the service
|
||||
RESTful Notes is written using http://projects.spring.io/spring-boot[Spring Boot] which
|
||||
makes it easy to get it up and running so that you can start exploring the REST API.
|
||||
|
||||
At the moment, binaries for RESTful Notes are not published anywhere. However, building
|
||||
and running it from source is straightforward. The first step is to clone the Git
|
||||
repository:
|
||||
|
||||
[source,bash]
|
||||
----
|
||||
$ git clone https://github.com/wilkinsona/spring-restdocs
|
||||
----
|
||||
|
||||
Once the clone is complete, you're ready to get the service up and running:
|
||||
|
||||
[source,bash]
|
||||
----
|
||||
$ cd rest-notes
|
||||
$ ./gradlew build
|
||||
$ java -jar build/libs/*.jar
|
||||
----
|
||||
|
||||
You can check that the service is up and running by executing a simple request using
|
||||
cURL:
|
||||
|
||||
include::{generated}/index/access_with_curl_request.asciidoc[]
|
||||
|
||||
This request should yield the following response:
|
||||
|
||||
include::{generated}/index/access_with_curl_response.asciidoc[]
|
||||
|
||||
Note the `_links` in the JSON response. They are key to navigating the API.
|
||||
|
||||
|
||||
|
||||
[getting-started-creating-a-note]
|
||||
== Creating a note
|
||||
Now that you've started the service and verified that it works, the next step is to use
|
||||
it to create a new note. As you saw above, the URI for working with notes is included as
|
||||
a link when you perform a `GET` request against the root of the service:
|
||||
|
||||
include::{generated}/index/access_with_curl_response.asciidoc[]
|
||||
|
||||
To create a note you need to execute a `POST` request to this URI, including a JSON
|
||||
payload containing the title and body of the note:
|
||||
|
||||
include::{generated}/notes/create_with_curl_request.asciidoc[]
|
||||
|
||||
The response from this request should have a status code of `201 Created` and contain a
|
||||
`Location` header whose value is the URI of the newly created note:
|
||||
|
||||
include::{generated}/notes/create_with_curl_response.asciidoc[]
|
||||
|
||||
To work with the newly created note you use the URI in the `Location` header. For example
|
||||
you can access the note's details by performing a `GET` request:
|
||||
|
||||
include::{generated}/notes/get_with_curl_request.asciidoc[]
|
||||
|
||||
This request will produce a response with the note's details in its body:
|
||||
|
||||
include::{generated}/notes/get_with_curl_response.asciidoc[]
|
||||
|
||||
Note the `tags` link which we'll make use of later.
|
||||
|
||||
|
||||
|
||||
[getting-started-creating-a-tag]
|
||||
== Creating a tag
|
||||
To make a note easier to find, it can be associated with any number of tags. To be able
|
||||
to tag a note, you must first create the tag.
|
||||
|
||||
Referring back to the response for the service's index, the URI for working with tags is
|
||||
include as a link:
|
||||
|
||||
include::{generated}/index/access_with_curl_response.asciidoc[]
|
||||
|
||||
To create a tag you need to execute a `POST` request to this URI, including a JSON
|
||||
payload containing the name of the tag:
|
||||
|
||||
include::{generated}/tags/create_with_curl_request.asciidoc[]
|
||||
|
||||
The response from this request should have a status code of `201 Created` and contain a
|
||||
`Location` header whose value is the URI of the newly created tag:
|
||||
|
||||
include::{generated}/tags/create_with_curl_response.asciidoc[]
|
||||
|
||||
To work with the newly created tag you use the URI in the `Location` header. For example
|
||||
you can access the tag's details by performing a `GET` request:
|
||||
|
||||
include::{generated}/tags/get_with_curl_request.asciidoc[]
|
||||
|
||||
This request will produce a response with the tag's details in its body:
|
||||
|
||||
include::{generated}/tags/get_with_curl_response.asciidoc[]
|
||||
|
||||
|
||||
|
||||
[getting-started-tagging-a-note]
|
||||
== Tagging a note
|
||||
A tag isn't particularly useful until it's been associated with one or more notes. There
|
||||
are two ways to tag a note: when the note is first created or by updating an existing
|
||||
note. We'll look at both of these in turn.
|
||||
|
||||
|
||||
|
||||
[getting-started-tagging-a-note-creating]
|
||||
=== Creating a tagged note
|
||||
The process is largely the same as we saw before, but this time, in addition to providing
|
||||
a title and body for the note, we'll also provide the tag that we want to be associated
|
||||
with it.
|
||||
|
||||
Once again we execute a `POST` request, but this time, in an array named tags, we include
|
||||
the URI of the tag we just created:
|
||||
|
||||
include::{generated}/notes/create_tagged_with_curl_request.asciidoc[]
|
||||
|
||||
Once again, the response's `Location` header tells use the URI of the newly created note:
|
||||
|
||||
include::{generated}/notes/create_tagged_with_curl_response.asciidoc[]
|
||||
|
||||
As before, a `GET` request executed against this URI will retrieve the note's details:
|
||||
|
||||
include::{generated}/notes/get_tagged_with_curl.asciidoc[]
|
||||
|
||||
To see the note's tags, execute a `GET` request against the URI of the note's tags link:
|
||||
|
||||
include::{generated}/notes/get_tags_for_tagged_with_curl_request.asciidoc[]
|
||||
|
||||
The response shows that, as expected, the note has a single tag:
|
||||
|
||||
include::{generated}/notes/get_tags_for_tagged_with_curl_response.asciidoc[]
|
||||
|
||||
|
||||
|
||||
[getting-started-tagging-a-note-existing]
|
||||
=== Tagging an existing note
|
||||
An existing note can be tagged by executing a `PATCH` request against the note's URI with
|
||||
a body that contains the array of tags to be associated with the note. We'll used the
|
||||
URI of the untagged note that we created earlier:
|
||||
|
||||
include::{generated}/notes/tag_existing_with_curl_request.asciidoc[]
|
||||
|
||||
This request should produce a `200 OK` response:
|
||||
|
||||
include::{generated}/notes/tag_existing_with_curl_response.asciidoc[]
|
||||
|
||||
When we first created this note, we noted the tags link included in its details:
|
||||
|
||||
include::{generated}/notes/get_with_curl_response.asciidoc[]
|
||||
|
||||
We can use that link now and execute a `GET` request to see that the note now has a
|
||||
single tag:
|
||||
|
||||
include::{generated}/notes/get_tags_for_existing_with_curl.asciidoc[]
|
||||
@@ -0,0 +1,245 @@
|
||||
/*
|
||||
* 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.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.notNullValue;
|
||||
import static org.springframework.restdocs.core.RestDocumentationRequestPostProcessors.port;
|
||||
import static org.springframework.restdocs.core.RestDocumentationResultHandlers.documentCurlRequest;
|
||||
import static org.springframework.restdocs.core.RestDocumentationResultHandlers.documentCurlRequestAndResponse;
|
||||
import static org.springframework.restdocs.core.RestDocumentationResultHandlers.documentCurlResponse;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.patch;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.SpringApplicationConfiguration;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.test.context.web.WebAppConfiguration;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.test.web.servlet.MvcResult;
|
||||
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
|
||||
import org.springframework.web.context.WebApplicationContext;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.jayway.jsonpath.JsonPath;
|
||||
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@SpringApplicationConfiguration(classes = Application.class)
|
||||
@WebAppConfiguration
|
||||
public class GettingStartedDocumentation {
|
||||
|
||||
@Autowired
|
||||
private ObjectMapper objectMapper;
|
||||
|
||||
@Autowired
|
||||
private WebApplicationContext context;
|
||||
|
||||
private MockMvc mockMvc;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context).build();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void index() throws Exception {
|
||||
this.mockMvc
|
||||
.perform(get("/").with(port(8080)).accept(MediaType.APPLICATION_JSON))
|
||||
.andExpect(status().isOk()).andDo(print())
|
||||
.andExpect(jsonPath("links[?(@.rel==notes)]", is(notNullValue())))
|
||||
.andExpect(jsonPath("links[?(@.rel==tags)]", is(notNullValue())))
|
||||
.andDo(documentCurlRequest("index/access_with_curl_request.asciidoc"))
|
||||
.andDo(documentCurlResponse("index/access_with_curl_response.asciidoc"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void creatingANote() throws JsonProcessingException, Exception {
|
||||
String noteLocation = createNote();
|
||||
getNote(noteLocation);
|
||||
|
||||
String tagLocation = createTag();
|
||||
getTag(tagLocation);
|
||||
|
||||
String taggedNoteLocation = createTaggedNote(tagLocation);
|
||||
getTaggedNote(taggedNoteLocation);
|
||||
getTags(taggedNoteLocation);
|
||||
|
||||
tagExistingNote(noteLocation, tagLocation);
|
||||
getTaggedExistingNote(noteLocation);
|
||||
getTagsForExistingNote(noteLocation);
|
||||
}
|
||||
|
||||
private String createNote() throws Exception {
|
||||
Map<String, String> note = new HashMap<String, String>();
|
||||
note.put("title", "Note creation with cURL");
|
||||
note.put("body", "An example of how to create a note using cURL");
|
||||
|
||||
String noteLocation = this.mockMvc
|
||||
.perform(
|
||||
post("/notes").with(port(8080))
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content(objectMapper.writeValueAsString(note)))
|
||||
.andExpect(status().isCreated())
|
||||
.andExpect(header().string("Location", notNullValue()))
|
||||
.andDo(documentCurlRequest("notes/create_with_curl_request.asciidoc")
|
||||
.includeResponseHeaders())
|
||||
.andDo(documentCurlResponse("notes/create_with_curl_response.asciidoc")
|
||||
.includeResponseHeaders()).andReturn().getResponse()
|
||||
.getHeader("Location");
|
||||
return noteLocation;
|
||||
}
|
||||
|
||||
private void getNote(String noteLocation) throws Exception {
|
||||
this.mockMvc.perform(get(noteLocation).with(port(8080)))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("title", is(notNullValue())))
|
||||
.andExpect(jsonPath("body", is(notNullValue())))
|
||||
.andExpect(jsonPath("links[?(@.rel==tags)]", is(notNullValue())))
|
||||
.andDo(documentCurlRequest("notes/get_with_curl_request.asciidoc"))
|
||||
.andDo(documentCurlResponse("notes/get_with_curl_response.asciidoc"));
|
||||
}
|
||||
|
||||
private String createTag() throws Exception, JsonProcessingException {
|
||||
Map<String, String> tag = new HashMap<String, String>();
|
||||
tag.put("name", "getting-started");
|
||||
|
||||
String tagLocation = this.mockMvc
|
||||
.perform(
|
||||
post("/tags").with(port(8080))
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content(objectMapper.writeValueAsString(tag)))
|
||||
.andExpect(status().isCreated())
|
||||
.andExpect(header().string("Location", notNullValue()))
|
||||
.andDo(documentCurlRequest("tags/create_with_curl_request.asciidoc")
|
||||
.includeResponseHeaders())
|
||||
.andDo(documentCurlResponse("tags/create_with_curl_response.asciidoc")
|
||||
.includeResponseHeaders()).andReturn().getResponse()
|
||||
.getHeader("Location");
|
||||
return tagLocation;
|
||||
}
|
||||
|
||||
private void getTag(String tagLocation) throws Exception {
|
||||
this.mockMvc.perform(get(tagLocation).with(port(8080)))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("name", is(notNullValue())))
|
||||
.andExpect(jsonPath("links[?(@.rel==notes)]", is(notNullValue())))
|
||||
.andDo(documentCurlRequest("tags/get_with_curl_request.asciidoc"))
|
||||
.andDo(documentCurlResponse("tags/get_with_curl_response.asciidoc"));
|
||||
}
|
||||
|
||||
private String createTaggedNote(String tag) throws Exception {
|
||||
Map<String, Object> note = new HashMap<String, Object>();
|
||||
note.put("title", "Tagged note creation with cURL");
|
||||
note.put("body", "An example of how to create a tagged note using cURL");
|
||||
note.put("tags", Arrays.asList(tag));
|
||||
|
||||
String noteLocation = this.mockMvc
|
||||
.perform(
|
||||
post("/notes").with(port(8080))
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content(objectMapper.writeValueAsString(note)))
|
||||
.andExpect(status().isCreated())
|
||||
.andExpect(header().string("Location", notNullValue()))
|
||||
.andDo(documentCurlRequest(
|
||||
"notes/create_tagged_with_curl_request.asciidoc")
|
||||
.includeResponseHeaders())
|
||||
.andDo(documentCurlResponse(
|
||||
"notes/create_tagged_with_curl_response.asciidoc")
|
||||
.includeResponseHeaders()).andReturn().getResponse()
|
||||
.getHeader("Location");
|
||||
return noteLocation;
|
||||
}
|
||||
|
||||
private void getTaggedNote(String tagLocation) throws Exception {
|
||||
this.mockMvc
|
||||
.perform(get(tagLocation).with(port(8080)))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("title", is(notNullValue())))
|
||||
.andExpect(jsonPath("body", is(notNullValue())))
|
||||
.andExpect(jsonPath("links[?(@.rel==tags)]", is(notNullValue())))
|
||||
.andDo(documentCurlRequestAndResponse("notes/get_tagged_with_curl.asciidoc"));
|
||||
}
|
||||
|
||||
private void getTags(String taggedNoteLocation) throws Exception {
|
||||
String tagsLocation = getLink(this.mockMvc.perform(get(taggedNoteLocation))
|
||||
.andReturn(), "tags");
|
||||
this.mockMvc
|
||||
.perform(get(tagsLocation).with(port(8080)))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("links[?(@.rel==tag)]", is(notNullValue())))
|
||||
.andDo(documentCurlRequest("notes/get_tags_for_tagged_with_curl_request.asciidoc"))
|
||||
.andDo(documentCurlResponse("notes/get_tags_for_tagged_with_curl_response.asciidoc"));
|
||||
}
|
||||
|
||||
private void tagExistingNote(String noteLocation, String tagLocation)
|
||||
throws Exception {
|
||||
Map<String, Object> update = new HashMap<String, Object>();
|
||||
update.put("tags", Arrays.asList(tagLocation));
|
||||
|
||||
this.mockMvc
|
||||
.perform(
|
||||
patch(noteLocation).with(port(8080))
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content(objectMapper.writeValueAsString(update)))
|
||||
.andExpect(status().isOk())
|
||||
.andDo(documentCurlRequest(
|
||||
"notes/tag_existing_with_curl_request.asciidoc")
|
||||
.includeResponseHeaders())
|
||||
.andDo(documentCurlResponse(
|
||||
"notes/tag_existing_with_curl_response.asciidoc")
|
||||
.includeResponseHeaders());
|
||||
|
||||
}
|
||||
|
||||
private void getTaggedExistingNote(String tagLocation) throws Exception {
|
||||
this.mockMvc
|
||||
.perform(get(tagLocation).with(port(8080)))
|
||||
.andExpect(status().isOk())
|
||||
.andDo(documentCurlRequestAndResponse("notes/get_tagged_existing_with_curl.asciidoc"));
|
||||
}
|
||||
|
||||
private void getTagsForExistingNote(String taggedNoteLocation) throws Exception {
|
||||
String tagsLocation = getLink(this.mockMvc.perform(get(taggedNoteLocation))
|
||||
.andReturn(), "tags");
|
||||
this.mockMvc
|
||||
.perform(get(tagsLocation).with(port(8080)))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("links[?(@.rel==tag)]", is(notNullValue())))
|
||||
.andDo(documentCurlRequestAndResponse("notes/get_tags_for_existing_with_curl.asciidoc"));
|
||||
}
|
||||
|
||||
private String getLink(MvcResult result, String rel)
|
||||
throws UnsupportedEncodingException {
|
||||
return JsonPath.parse(result.getResponse().getContentAsString()).read(
|
||||
"links[?(@.rel==" + rel + ")][0].href");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
org.springframework.restdocs.outputDir = build/generated-documentation
|
||||
@@ -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,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 Application {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(Application.class, args);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* 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.RestController;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/")
|
||||
public class IndexController {
|
||||
|
||||
@RequestMapping
|
||||
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,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,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.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.data.repository.CrudRepository;
|
||||
|
||||
public interface NoteRepository extends CrudRepository<Note, Long> {
|
||||
|
||||
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("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,119 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/notes")
|
||||
public class NotesController {
|
||||
|
||||
private final NoteRepository noteRepository;
|
||||
|
||||
private final TagRepository tagRepository;
|
||||
|
||||
private final NoteResourceAssembler noteResourceAssembler;
|
||||
|
||||
@Autowired
|
||||
public NotesController(NoteRepository noteRepository, TagRepository tagRepository,
|
||||
NoteResourceAssembler noteResourceAssembler) {
|
||||
this.noteRepository = noteRepository;
|
||||
this.tagRepository = tagRepository;
|
||||
this.noteResourceAssembler = noteResourceAssembler;
|
||||
}
|
||||
|
||||
@RequestMapping
|
||||
Iterable<NoteResource> all() {
|
||||
return 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.GET)
|
||||
Resource<Note> note(@PathVariable("id") long id) {
|
||||
Note note = this.noteRepository.findOne(id);
|
||||
return this.noteResourceAssembler.toResource(note);
|
||||
}
|
||||
|
||||
@RequestMapping(value = "/{id}/tags", method = RequestMethod.GET)
|
||||
ResourceSupport noteTags(@PathVariable("id") long id) {
|
||||
ResourceSupport resource = new ResourceSupport();
|
||||
Note note = this.noteRepository.findOne(id);
|
||||
for (Tag tag : note.getTags()) {
|
||||
resource.add(linkTo(TagsController.class).slash(tag.getId()).withRel("tag"));
|
||||
}
|
||||
return resource;
|
||||
}
|
||||
|
||||
@RequestMapping(value = "/{id}", method = RequestMethod.PATCH)
|
||||
void updateNote(@PathVariable("id") long id, @RequestBody NotePatchInput noteInput) {
|
||||
Note note = this.noteRepository.findOne(id);
|
||||
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) {
|
||||
UriTemplate template = new UriTemplate("/tags/{id}");
|
||||
return tagLocations
|
||||
.stream()
|
||||
.map(location -> this.tagRepository.findOne(Long.valueOf(template.match(
|
||||
location.toASCIIString()).get("id"))))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
@@ -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,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,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 com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
public class TagInput {
|
||||
|
||||
private final String name;
|
||||
|
||||
@JsonCreator
|
||||
public TagInput(@JsonProperty("name") String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* 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.data.repository.CrudRepository;
|
||||
|
||||
public interface TagRepository extends CrudRepository<Tag, Long> {
|
||||
|
||||
}
|
||||
@@ -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("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,84 @@
|
||||
/*
|
||||
* 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.TagResourceAssembler.TagResource;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("tags")
|
||||
public class TagsController {
|
||||
|
||||
private final TagRepository repository;
|
||||
|
||||
private final TagResourceAssembler resourceAssembler;
|
||||
|
||||
@Autowired
|
||||
public TagsController(TagRepository repository, TagResourceAssembler resourceAssembler) {
|
||||
this.repository = repository;
|
||||
this.resourceAssembler = resourceAssembler;
|
||||
}
|
||||
|
||||
@RequestMapping
|
||||
Iterable<TagResource> all() {
|
||||
return this.resourceAssembler.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("/{id}")
|
||||
Resource<Tag> tag(@PathVariable("id") long id) {
|
||||
Tag tag = this.repository.findOne(id);
|
||||
return this.resourceAssembler.toResource(tag);
|
||||
}
|
||||
|
||||
@RequestMapping(value = "/{id}/notes", method = RequestMethod.GET)
|
||||
ResourceSupport tagNotes(@PathVariable("id") long id) {
|
||||
ResourceSupport resource = new ResourceSupport();
|
||||
Tag tag = this.repository.findOne(id);
|
||||
for (Note note : tag.getNotes()) {
|
||||
resource.add(linkTo(NotesController.class).slash(note.getId())
|
||||
.withRel("note"));
|
||||
}
|
||||
return resource;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user