diff --git a/partial-response/README.md b/partial-response/README.md new file mode 100644 index 000000000..5d03cb359 --- /dev/null +++ b/partial-response/README.md @@ -0,0 +1,29 @@ +--- +layout: pattern +title: Partial Response +folder: partial-response +permalink: /patterns/partial-response/ +categories: Architectural +tags: + - Java + - KISS + - YAGNI + - Difficulty-Beginner +--- + +## Intent +Send partial response from server to client on need basis. Client will specify the the fields +that it need to server, instead of serving all details for resource. + +![alt text](./etc/partial-response.urm.png "partial-response") + +## Applicability +Use the Partial Response pattern when + +* Client need only subset of data from resource. +* To avoid too much data transfer over wire + +## Credits + +* [Common Design Patterns](https://cloud.google.com/apis/design/design_patterns) +* [Partial Response in RESTful API Design](http://yaoganglian.com/2013/07/01/partial-response/) diff --git a/partial-response/etc/partial-response.ucls b/partial-response/etc/partial-response.ucls new file mode 100644 index 000000000..a0b029496 --- /dev/null +++ b/partial-response/etc/partial-response.ucls @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/partial-response/etc/partial-response.urm.png b/partial-response/etc/partial-response.urm.png new file mode 100644 index 000000000..17dbd5f1b Binary files /dev/null and b/partial-response/etc/partial-response.urm.png differ diff --git a/partial-response/etc/partial-response.urm.puml b/partial-response/etc/partial-response.urm.puml new file mode 100644 index 000000000..69efd0454 --- /dev/null +++ b/partial-response/etc/partial-response.urm.puml @@ -0,0 +1,31 @@ +@startuml +package com.iluwatar.partialresponse { + class FieldJsonMapper { + + FieldJsonMapper() + - getString(video : Video, declaredField : Field) : String + + toJson(video : Video, fields : String[]) : String + } + class Video { + - description : String + - director : String + - id : Integer + - language : String + - length : Integer + - title : String + + Video(id : Integer, title : String, length : Integer, description : String, director : String, language : String) + + toString() : String + } + class VideoClientApp { + - LOGGER : Logger {static} + + VideoClientApp() + + main(args : String[]) {static} + } + class VideoResource { + - fieldJsonMapper : FieldJsonMapper + - videos : Map + + VideoResource(fieldJsonMapper : FieldJsonMapper, videos : Map) + + getDetails(id : Integer, fields : String[]) : String + } +} +VideoResource --> "-fieldJsonMapper" FieldJsonMapper +@enduml \ No newline at end of file diff --git a/partial-response/pom.xml b/partial-response/pom.xml new file mode 100644 index 000000000..7ce36ecec --- /dev/null +++ b/partial-response/pom.xml @@ -0,0 +1,25 @@ + + + + java-design-patterns + com.iluwatar + 1.17.0-SNAPSHOT + + 4.0.0 + + partial-response + + + junit + junit + + + org.mockito + mockito-core + + + + + \ No newline at end of file diff --git a/partial-response/src/main/java/com/iluwatar/partialresponse/App.java b/partial-response/src/main/java/com/iluwatar/partialresponse/App.java new file mode 100644 index 000000000..2977b50a7 --- /dev/null +++ b/partial-response/src/main/java/com/iluwatar/partialresponse/App.java @@ -0,0 +1,74 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2014-2017 Gopinath Langote + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.iluwatar.partialresponse; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.HashMap; +import java.util.Map; + +/** + * The Partial response pattern is a design pattern in which client specifies fields to fetch to serve. + * Here {@link App} is playing as client for {@link VideoResource} server. + * Client ask for specific fields information in video to server. + *

+ *

+ * {@link VideoResource} act as server to serve video information. + */ + +public class App { + private static final Logger LOGGER = LoggerFactory.getLogger(App.class); + + /** + * Method as act client and request to server for video details. + * + * @param args program argument. + */ + public static void main(String[] args) throws Exception { + Map videos = new HashMap<>(); + videos.put(1, new Video(1, "Avatar", 178, "epic science fiction film", "James Cameron", "English")); + videos.put(2, new Video(2, "Godzilla Resurgence", 120, "Action & drama movie|", "Hideaki Anno", "Japanese")); + videos.put(3, new Video(3, "Interstellar", 169, "Adventure & Sci-Fi", "Christopher Nolan", "English")); + VideoResource videoResource = new VideoResource(new FieldJsonMapper(), videos); + + + LOGGER.info("Retrieving full response from server:-"); + LOGGER.info("Get all video information:"); + String videoDetails = videoResource.getDetails(1); + LOGGER.info(videoDetails); + + LOGGER.info("----------------------------------------------------------"); + + LOGGER.info("Retrieving partial response from server:-"); + LOGGER.info("Get video @id, @title, @director:"); + String specificFieldsDetails = videoResource.getDetails(3, "id", "title", "director"); + LOGGER.info(specificFieldsDetails); + + LOGGER.info("Get video @id, @length:"); + String videoLength = videoResource.getDetails(3, "id", "length"); + LOGGER.info(videoLength); + } +} diff --git a/partial-response/src/main/java/com/iluwatar/partialresponse/FieldJsonMapper.java b/partial-response/src/main/java/com/iluwatar/partialresponse/FieldJsonMapper.java new file mode 100644 index 000000000..9283cfa69 --- /dev/null +++ b/partial-response/src/main/java/com/iluwatar/partialresponse/FieldJsonMapper.java @@ -0,0 +1,60 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2014-2017 Gopinath Langote + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.iluwatar.partialresponse; + +import java.lang.reflect.Field; + +/** + * Map a video to json + */ +public class FieldJsonMapper { + + /** + * @param video object containing video information + * @param fields fields information to get + * @return json of required fields from video + */ + public String toJson(Video video, String[] fields) throws Exception { + StringBuilder json = new StringBuilder().append("{"); + + for (int i = 0, fieldsLength = fields.length; i < fieldsLength; i++) { + json.append(getString(video, Video.class.getDeclaredField(fields[i]))); + if (i != fieldsLength - 1) { + json.append(","); + } + } + json.append("}"); + return json.toString(); + } + + private String getString(Video video, Field declaredField) throws IllegalAccessException { + declaredField.setAccessible(true); + Object value = declaredField.get(video); + if (declaredField.get(video) instanceof Integer) { + return "\"" + declaredField.getName() + "\"" + ": " + value; + } + return "\"" + declaredField.getName() + "\"" + ": " + "\"" + value.toString() + "\""; + } +} diff --git a/partial-response/src/main/java/com/iluwatar/partialresponse/Video.java b/partial-response/src/main/java/com/iluwatar/partialresponse/Video.java new file mode 100644 index 000000000..e242965e3 --- /dev/null +++ b/partial-response/src/main/java/com/iluwatar/partialresponse/Video.java @@ -0,0 +1,70 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2014-2017 Gopinath Langote + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.iluwatar.partialresponse; + +/** + * {@link Video} is a entity to serve from server.It contains all video related information.. + *

+ */ +public class Video { + private final Integer id; + private final String title; + private final Integer length; + private final String description; + private final String director; + private final String language; + + /** + * @param id video unique id + * @param title video title + * @param length video length in minutes + * @param description video description by publisher + * @param director video director name + * @param language video language {private, public} + */ + public Video(Integer id, String title, Integer length, String description, String director, String language) { + this.id = id; + this.title = title; + this.length = length; + this.description = description; + this.director = director; + this.language = language; + } + + /** + * @return json representaion of video + */ + @Override + public String toString() { + return "{" + + "\"id\": " + id + "," + + "\"title\": \"" + title + "\"," + + "\"length\": " + length + "," + + "\"description\": \"" + description + "\"," + + "\"director\": \"" + director + "\"," + + "\"language\": \"" + language + "\"," + + "}"; + } +} diff --git a/partial-response/src/main/java/com/iluwatar/partialresponse/VideoResource.java b/partial-response/src/main/java/com/iluwatar/partialresponse/VideoResource.java new file mode 100644 index 000000000..2bf7a73c1 --- /dev/null +++ b/partial-response/src/main/java/com/iluwatar/partialresponse/VideoResource.java @@ -0,0 +1,57 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2014-2017 Gopinath Langote + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.iluwatar.partialresponse; + +import java.util.Map; + +/** + * The resource class which serves video information. + * This class act as server in the demo. Which has all video details. + */ +public class VideoResource { + private FieldJsonMapper fieldJsonMapper; + private Map videos; + + /** + * @param fieldJsonMapper map object to json. + * @param videos initialize resource with existing videos. Act as database. + */ + public VideoResource(FieldJsonMapper fieldJsonMapper, Map videos) { + this.fieldJsonMapper = fieldJsonMapper; + this.videos = videos; + } + + /** + * @param id video id + * @param fields fields to get information about + * @return full response if no fields specified else partial response for given field. + */ + public String getDetails(Integer id, String... fields) throws Exception { + if (fields.length == 0) { + return videos.get(id).toString(); + } + return fieldJsonMapper.toJson(videos.get(id), fields); + } +} diff --git a/partial-response/src/test/java/com/iluwatar/partialresponse/AppTest.java b/partial-response/src/test/java/com/iluwatar/partialresponse/AppTest.java new file mode 100644 index 000000000..2ac34dd0d --- /dev/null +++ b/partial-response/src/test/java/com/iluwatar/partialresponse/AppTest.java @@ -0,0 +1,40 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2014-2017 Gopinath Langote + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.iluwatar.partialresponse; + +import org.junit.Test; + +/** + * Application test + */ +public class AppTest { + + @Test + public void main() throws Exception { + String[] args = {}; + App.main(args); + } + +} \ No newline at end of file diff --git a/partial-response/src/test/java/com/iluwatar/partialresponse/FieldJsonMapperTest.java b/partial-response/src/test/java/com/iluwatar/partialresponse/FieldJsonMapperTest.java new file mode 100644 index 000000000..9bc078b57 --- /dev/null +++ b/partial-response/src/test/java/com/iluwatar/partialresponse/FieldJsonMapperTest.java @@ -0,0 +1,53 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2014-2017 Gopinath Langote + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.iluwatar.partialresponse; + +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +/** + * tests {@link FieldJsonMapper}. + */ +public class FieldJsonMapperTest { + private FieldJsonMapper mapper; + + @Before + public void setUp() { + mapper = new FieldJsonMapper(); + } + + @Test + public void shouldReturnJsonForSpecifiedFieldsInVideo() throws Exception { + String[] fields = new String[]{"id", "title", "length"}; + Video video = new Video(2, "Godzilla Resurgence", 120, "Action & drama movie|", "Hideaki Anno", "Japanese"); + + String jsonFieldResponse = mapper.toJson(video, fields); + + String expectedDetails = "{\"id\": 2,\"title\": \"Godzilla Resurgence\",\"length\": 120}"; + assertEquals(expectedDetails, jsonFieldResponse); + } +} \ No newline at end of file diff --git a/partial-response/src/test/java/com/iluwatar/partialresponse/VideoResourceTest.java b/partial-response/src/test/java/com/iluwatar/partialresponse/VideoResourceTest.java new file mode 100644 index 000000000..0453e8ffd --- /dev/null +++ b/partial-response/src/test/java/com/iluwatar/partialresponse/VideoResourceTest.java @@ -0,0 +1,80 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2014-2017 Gopinath Langote + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.iluwatar.partialresponse; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; + +import java.util.HashMap; +import java.util.Map; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.when; + +/** + * tests {@link VideoResource}. + */ +@RunWith(MockitoJUnitRunner.class) +public class VideoResourceTest { + @Mock + private FieldJsonMapper fieldJsonMapper; + + private VideoResource resource; + + @Before + public void setUp() { + Map videos = new HashMap<>(); + videos.put(1, new Video(1, "Avatar", 178, "epic science fiction film", "James Cameron", "English")); + videos.put(2, new Video(2, "Godzilla Resurgence", 120, "Action & drama movie|", "Hideaki Anno", "Japanese")); + videos.put(3, new Video(3, "Interstellar", 169, "Adventure & Sci-Fi", "Christopher Nolan", "English")); + resource = new VideoResource(fieldJsonMapper, videos); + } + + @Test + public void shouldGiveVideoDetailsById() throws Exception { + String actualDetails = resource.getDetails(1); + + String expectedDetails = "{\"id\": 1,\"title\": \"Avatar\",\"length\": 178,\"description\": " + + "\"epic science fiction film\",\"director\": \"James Cameron\",\"language\": \"English\",}"; + assertEquals(expectedDetails, actualDetails); + } + + @Test + public void shouldGiveSpecifiedFieldsInformationOfVideo() throws Exception { + String[] fields = new String[]{"id", "title", "length"}; + + String expectedDetails = "{\"id\": 1,\"title\": \"Avatar\",\"length\": 178}"; + when(fieldJsonMapper.toJson(any(Video.class), eq(fields))).thenReturn(expectedDetails); + + String actualFieldsDetails = resource.getDetails(2, fields); + + assertEquals(expectedDetails, actualFieldsDetails); + } +} \ No newline at end of file diff --git a/pom.xml b/pom.xml index 840c14eca..ed9981b34 100644 --- a/pom.xml +++ b/pom.xml @@ -146,7 +146,8 @@ event-sourcing data-transfer-object throttling - + partial-response +