├── .github
├── CONTRIBUTING.md
├── ISSUE_TEMPLATE.md
├── PULL_REQUEST_TEMPLATE.md
└── workflows
│ └── main.yaml
├── .gitignore
├── .travis.yml
├── Dockerfile
├── LICENSE
├── README.md
├── api-spec
├── carts.json
├── hooks.js
├── mock.json
└── routes.json
├── docker-compose-zipkin.yml
├── docker
└── carts
│ └── Dockerfile
├── pom.xml
├── scripts
├── build.sh
└── push.sh
├── src
├── main
│ ├── java
│ │ └── works
│ │ │ └── weave
│ │ │ └── socks
│ │ │ └── cart
│ │ │ ├── CartApplication.java
│ │ │ ├── action
│ │ │ └── FirstResultOrDefault.java
│ │ │ ├── cart
│ │ │ ├── CartContentsResource.java
│ │ │ ├── CartDAO.java
│ │ │ ├── CartResource.java
│ │ │ ├── Contents.java
│ │ │ ├── HasContents.java
│ │ │ └── Resource.java
│ │ │ ├── configuration
│ │ │ ├── BeanConfiguration.java
│ │ │ ├── MongoConfiguration.java
│ │ │ ├── ValidationConfiguration.java
│ │ │ └── WebMvcConfig.java
│ │ │ ├── controllers
│ │ │ ├── CartsController.java
│ │ │ ├── HealthCheckController.java
│ │ │ └── ItemsController.java
│ │ │ ├── entities
│ │ │ ├── Cart.java
│ │ │ ├── HealthCheck.java
│ │ │ └── Item.java
│ │ │ ├── item
│ │ │ ├── FoundItem.java
│ │ │ ├── ItemDAO.java
│ │ │ └── ItemResource.java
│ │ │ ├── middleware
│ │ │ └── HTTPMonitoringInterceptor.java
│ │ │ └── repositories
│ │ │ ├── CartRepository.java
│ │ │ └── ItemRepository.java
│ └── resources
│ │ └── application.properties
└── test
│ └── java
│ └── works
│ └── weave
│ └── socks
│ └── cart
│ ├── action
│ └── UnitFirstResultOrDefault.java
│ ├── cart
│ ├── UnitCartContentsResource.java
│ └── UnitCartResource.java
│ ├── controllers
│ ├── UnitCartsController.java
│ ├── UnitHealthCheckController.java
│ └── UnitItemsController.java
│ ├── item
│ ├── UnitFoundItem.java
│ └── UnitItemResource.java
│ └── repositories
│ ├── ITCartRepository.java
│ └── ITItemRepository.java
└── test
├── Dockerfile
├── component.py
├── container.py
├── coveralls.py
├── test.sh
├── unit.py
└── util
├── Api.py
├── Docker.py
├── Dredd.py
└── __init__.py
/.github/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contribution guidelines
2 |
3 | We'd love to accept your contributions; large or small. Simply submit an issue or pull request via Github and involve one of the active members. Simple! But please read the rest of this document to ensure we're all on the same page.
4 |
5 | ## General Rules
6 |
7 | - Be kind and polite. Written language often does not convey the sentiment, so make sure you use lots of jokes and emoticons to get the sentiment across.
8 | - Prefer best practice. Everyone has their preferred style, but try to conform to current best practices. We don't enforce any strict rules.
9 | - Test your code to the best of your abilities. See the testing documentation for the correct scope of your test.
10 |
11 | ## Bug reports or feature requests
12 |
13 | Please open an issue if you have found an issue or have an idea for a new feature. Please follow the bug reporting guidelines if you submit an issue.
14 |
15 | ## New Contributors
16 |
17 | We have a list of issues on Github with "HelpWanted" labels attributed to them. These represent tasks that we don't have time to do, are self-contained and relatively easy to implement. If you'd like to contribute, but don't know where to start, [look here](https://github.com/microservices-demo/microservices-demo/issues?utf8=%E2%9C%93&q=is%3Aissue%20is%3Aopen%20label%3AHelpWanted).
18 |
19 | ## Direction
20 |
21 | This project does have a general direction, but we're happy to consider deviating or pivoting from the direction we're currently heading. See the introductory material for details regarding direction.
22 |
23 | With that said, there is absolutely nothing stopping you from submitting a PR. If you've taken the effort to contribute, someone will make the effort to review.
24 |
25 | ## License
26 |
27 | This project is Apache v2.0 licenced. Submitting and merging a PR implies you accept these terms.
28 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | - Add labels appropriate to the issue
2 | - Describe the expected behaviour and the actual behaviour
3 | - Describe steps to reproduce the problem
4 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | - Read the contribution guidelines
2 | - Include a reference to a related issue in this repository
3 | - A description of the changes proposed in the pull request
--------------------------------------------------------------------------------
/.github/workflows/main.yaml:
--------------------------------------------------------------------------------
1 | name: ci
2 |
3 | on:
4 | push:
5 | branches:
6 | - "*" # run for branches
7 | tags:
8 | - "*" # run for tags
9 | pull_request:
10 | branches:
11 | - "*" # run for branches
12 | tags:
13 | - "*" # run for tags
14 |
15 | jobs:
16 | test:
17 | runs-on: ubuntu-latest
18 | env:
19 | GROUP: weaveworksdemos
20 | COMMIT: ${{ github.sha }}
21 | REPO: carts
22 | steps:
23 | - uses: actions/checkout@v2
24 |
25 |
26 | - name: Set up JDK 1.8
27 | uses: actions/setup-java@v1
28 | with:
29 | java-version: 1.8
30 |
31 | #
32 | #
33 | # Build
34 | - name: Build jar files
35 | run: ./scripts/build.sh
36 |
37 | #
38 | #
39 | # Push to dockerhub
40 | - name: Push to Docker Hub
41 | uses: docker/build-push-action@v1
42 | if: startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/master'
43 | with:
44 | username: ${{ secrets.DOCKER_USER }}
45 | password: ${{ secrets.DOCKER_PASS }}
46 | repository: ${{ env.GROUP }}/${{ env.REPO }}
47 | tag_with_ref: true
48 | tag_with_sha: true
49 | path: docker/carts
50 | dockerfile: docker/carts/Dockerfile
51 |
52 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 |
6 | # Dependency directories
7 | node_modules
8 | jspm_packages
9 |
10 | # Optional npm cache directory
11 | .npm
12 |
13 | ### Go template
14 | # Compiled Object files, Static and Dynamic libs (Shared Objects)
15 | *.o
16 | *.a
17 | *.so
18 |
19 | # Folders
20 | _obj
21 | _test
22 |
23 | # Architecture specific extensions/prefixes
24 | *.[568vq]
25 | [568vq].out
26 |
27 | *.cgo1.go
28 | *.cgo2.c
29 | _cgo_defun.c
30 | _cgo_gotypes.go
31 | _cgo_export.*
32 |
33 | _testmain.go
34 |
35 | *.exe
36 | *.test
37 | *.prof
38 | ### Java template
39 | /target/
40 | *.class
41 |
42 | # Mobile Tools for Java (J2ME)
43 | .mtj.tmp/
44 |
45 | # Package Files #
46 | *.war
47 | *.ear
48 |
49 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
50 | hs_err_pid*
51 | ### OSX template
52 | .DS_Store
53 | .AppleDouble
54 | .LSOverride
55 |
56 | # Icon must end with two \r
57 | Icon
58 |
59 | # Thumbnails
60 | ._*
61 |
62 | # Files that might appear in the root of a volume
63 | .DocumentRevisions-V100
64 | .fseventsd
65 | .Spotlight-V100
66 | .TemporaryItems
67 | .Trashes
68 | .VolumeIcon.icns
69 |
70 | # Directories potentially created on remote AFP share
71 | .AppleDB
72 | .AppleDesktop
73 | Network Trash Folder
74 | Temporary Items
75 | .apdisk
76 | ### JetBrains template
77 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
78 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
79 |
80 | # User-specific stuff:
81 | .idea
82 |
83 | ## File-based project format:
84 | *.iws
85 | *.iml
86 |
87 | ## Plugin-specific files:
88 |
89 | # IntelliJ
90 | /out/
91 |
92 | # mpeltonen/sbt-idea plugin
93 | .idea_modules/
94 |
95 | # JIRA plugin
96 | atlassian-ide-plugin.xml
97 |
98 | # Crashlytics plugin (for Android Studio and IntelliJ)
99 | com_crashlytics_export_strings.xml
100 | crashlytics.properties
101 | crashlytics-build.properties
102 | fabric.properties
103 | # Created by .ignore support plugin (hsz.mobi)
104 |
105 | # Maven builds
106 | */target
107 | */*/target
108 |
109 | # AWS ECS install scripts generates an SSH key file
110 | weave-ecs-demo-key.pem
111 |
112 | # Load test generates pyc files
113 | *.pyc
114 |
115 | # Ignore Vagrant cache files
116 | *.vagrant/
117 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: java
2 | sudo: required
3 | services:
4 | - docker
5 | jdk:
6 | - oraclejdk11
7 | install: true
8 |
9 | env:
10 | - GROUP=weaveworksdemos COMMIT=$TRAVIS_COMMIT TAG=$TRAVIS_TAG REPO=carts;
11 |
12 | script:
13 | - set -e
14 | - travis_wait ./scripts/build.sh;
15 |
16 | after_success:
17 | - set -e;
18 | - if [ -z "$DOCKER_PASS" ] ; then
19 | echo "This is a build triggered by an external PR. Skipping docker push.";
20 | exit 0;
21 | fi;
22 | - docker login -u $DOCKER_USER -p $DOCKER_PASS;
23 | - ./scripts/push.sh
24 | notifications:
25 | slack:
26 | secure: jHh+ksay1dxGEMxxep6XllCR5VwsmuSyIpLgG9bUKpG4fPR948K5Ev2E/vyobrqbTi5JMWasXd3ecwzV1QIN8QvvQ33Fx70oSp9Cit5gvNmLWVplHkWiMnnLD33IMCbtDHK69FjH3GQyJ5HfgjiJSF2nTFaCArYImrCcnU+ENTXW9L3rlYm5ElnP/NfWbrqxIF4EOiCKHMM5kpFYWO8mYLTVilHhPLAz94IVV2OUTe4NUdBJgXolAJqg28QTcfrk5x0OudDTc3Ssa3f/ezrE0LgrzeWdekcrNdYt+YZYaQxiPOIhNyZu0RxU+46ip4XhUS6qSXxW1Kpf0RXkgdmTTbYxF31D4GG+SoTjbBONZqK+qw3AkuYxTQYZnl50n+Hd1gtKY31qCwFEAVUU/tYDI5oKUdUaDsiHU5J/Yt+YMPoyWZ7FOZYwSNy2xs78XGXqQnYn35lsBv0BmYo/1mPUFbrlZgVO5b5wki7NhuLKnqHo9GvkavG71py1VjJTShq5um4NrPBYZ/Y9aZtZD6E2BQPJBgciwGtoFdmaTzmPtwPkWMA+SlcAtpcQlFATXM7xS5324XYK9+okDoqybj1cmHIk1FLnM9nx3r+TUOGsUrlY2ZO9FIrljgII3H8vzcUCbwMwzaQvhsBCFBFZQ6umjbHxnRN9zRe52c4zkkCSAZc=
27 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM java:openjdk-8-alpine
2 |
3 | WORKDIR /usr/src/app
4 | COPY ./target/*.jar ./app.jar
5 |
6 | ENTRYPOINT ["java","-Djava.security.egd=file:/dev/urandom","-jar","./app.jar", "--port=80"]
7 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "{}"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright {yyyy} {name of copyright owner}
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://travis-ci.org/microservices-demo/carts) [](https://coveralls.io/github/microservices-demo/carts?branch=master)
2 | [](http://microbadger.com/images/weaveworksdemos/cart "Get your own image badge on microbadger.com")
3 |
4 | # DEPRECATED: cart
5 | A microservices-demo service that provides shopping carts for users.
6 |
7 | This build is built, tested and released by travis.
8 |
9 | # API Spec
10 |
11 | Checkout the API Spec [here](http://microservices-demo.github.io/api/index?url=https://raw.githubusercontent.com/microservices-demo/carts/master/api-spec/cart.json)
12 |
13 | # Build
14 |
15 | ## Java
16 |
17 | `mvn -DskipTests package`
18 |
19 | ## Docker
20 |
21 | `GROUP=weaveworksdemos COMMIT=test ./scripts/build.sh`
22 |
23 | # Test
24 |
25 | `./test/test.sh < python testing file >`. For example: `./test/test.sh unit.py`
26 |
27 | # Run
28 |
29 | `mvn spring-boot:run`
30 |
31 | # Check
32 |
33 | `curl http://localhost:8081/health`
34 |
35 | # Use
36 |
37 | `curl http://localhost:8081`
38 |
39 | # Push
40 |
41 | `GROUP=weaveworksdemos COMMIT=test ./scripts/push.sh`
42 |
--------------------------------------------------------------------------------
/api-spec/carts.json:
--------------------------------------------------------------------------------
1 | {
2 | "swagger": "2.0",
3 | "info": {
4 | "version": "",
5 | "title": "Carts and items",
6 | "description": "Carts and items resources",
7 | "license": {
8 | "name": "MIT",
9 | "url": "http://github.com/gruntjs/grunt/blob/master/LICENSE-MIT"
10 | }
11 | },
12 | "host": "carts",
13 | "basePath": "/",
14 | "securityDefinitions": {},
15 | "schemes": [
16 | "http"
17 | ],
18 | "consumes": [
19 | "application/json;charset=UTF-8",
20 | "text/plain"
21 | ],
22 | "paths": {
23 | "/carts/{customerId}": {
24 | "get": {
25 | "description": "",
26 | "operationId": "Get cart",
27 | "produces": [
28 | "application/json;charset=UTF-8"
29 | ],
30 | "parameters": [
31 | {
32 | "name": "customerId",
33 | "in": "path",
34 | "required": true,
35 | "type": "string",
36 | "x-example": "1"
37 | }
38 | ],
39 | "responses": {
40 | "200": {
41 | "description": "Returns cart",
42 | "schema": {
43 | "$ref": "#/definitions/Getcartresponse"
44 | }
45 | }
46 | }
47 | },
48 | "delete": {
49 | "description": "",
50 | "operationId": "Delete cart",
51 | "parameters": [
52 | {
53 | "name": "customerId",
54 | "in": "path",
55 | "required": true,
56 | "type": "string",
57 | "x-example": "1"
58 | }
59 | ],
60 | "responses": {
61 | "202": {
62 | "description": ""
63 | }
64 | }
65 | }
66 | },
67 | "/carts/{customerId}/items": {
68 | "post": {
69 | "description": "",
70 | "operationId": "Add an item to the cart",
71 | "produces": [
72 | "application/json;charset=UTF-8"
73 | ],
74 | "parameters": [
75 | {
76 | "name": "customerId",
77 | "in": "path",
78 | "required": true,
79 | "type": "string",
80 | "x-example": "579f21ae98684924944651bf"
81 | },
82 | {
83 | "name": "body",
84 | "in": "body",
85 | "required": true,
86 | "schema": {
87 | "$ref": "#/definitions/CartItem",
88 | "example": {
89 | "itemId":"819e1fbf-8b7e-4f6d-811f-693534916a8b",
90 | "quantity": 20,
91 | "unitPrice" : 99.0
92 | }
93 | }
94 | }
95 | ],
96 | "responses": {
97 | "201": {
98 | "description": "",
99 | "schema": {
100 | "$ref": "#/definitions/CartItem"
101 | }
102 | }
103 | }
104 | },
105 | "patch": {
106 | "description": "Update an item",
107 | "operationId": "Update item",
108 | "produces": [
109 | "application/json;charset=UTF-8"
110 | ],
111 | "parameters": [
112 | {
113 | "name": "customerId",
114 | "in": "path",
115 | "required": true,
116 | "type": "string",
117 | "x-example": "579f21ae98684924944651bf"
118 | },
119 | {
120 | "name": "body",
121 | "in": "body",
122 | "required": true,
123 | "schema": {
124 | "type": "object"
125 | }
126 | }
127 | ],
128 | "responses": {
129 | "200": {
130 | "description": ""
131 | }
132 | }
133 | }
134 | },
135 | "/carts/{customerId}/items/{itemId}": {
136 | "delete": {
137 | "description": "Delete cart item",
138 | "operationId": "delete",
139 | "parameters": [
140 | {
141 | "name": "itemId",
142 | "in": "path",
143 | "required": true,
144 | "type": "string",
145 | "x-example": "819e1fbf-8b7e-4f6d-811f-693534916a8b"
146 | },
147 | {
148 | "name": "customerId",
149 | "in": "path",
150 | "required": true,
151 | "type": "string",
152 | "x-example": "579f21ae98684924944651bf"
153 | }
154 | ],
155 | "responses": {
156 | "202": {
157 | "description": "Delete response"
158 | }
159 | }
160 | }
161 | }
162 | },
163 | "definitions": {
164 | "Getcartresponse": {
165 | "title": "Get cart response",
166 | "type": "object",
167 | "properties": {
168 | "customerId": {
169 | "type": "string"
170 | }
171 | },
172 | "required": [
173 | "customerId"
174 | ]
175 | },
176 | "CartItem": {
177 | "title": "Cart item",
178 | "type": "object",
179 | "properties": {
180 | "itemId": {
181 | "type": "string"
182 | },
183 | "quantity": {
184 | "type": "integer"
185 | },
186 | "unitPrice": {
187 | "type": "number"
188 | }
189 | },
190 | "required": [
191 | "itemId",
192 | "quantity",
193 | "unitPrice"
194 | ]
195 | }
196 | }
197 | }
198 |
--------------------------------------------------------------------------------
/api-spec/hooks.js:
--------------------------------------------------------------------------------
1 | const hooks = require('hooks');
2 | const {MongoClient} = require('mongodb');
3 | const ObjectID = require('mongodb').ObjectID;
4 |
5 | let db;
6 |
7 | const address = [
8 | {"_id":ObjectID("579f21ae98684924944651bd"),"_class":"works.weave.socks.users.entities.Address","number":"69","street":"Wilson Street","city":"Hartlepool","postcode":"TS26 8JU","country":"United Kingdom"},
9 | {"_id":ObjectID("579f21ae98684924944651c0"),"_class":"works.weave.socks.users.entities.Address","number":"122","street":"Radstone WayNet","city":"Northampton","postcode":"NN2 8NT","country":"United Kingdom"},
10 | {"_id":ObjectID("579f21ae98684924944651c3"),"_class":"works.weave.socks.users.entities.Address","number":"3","street":"Radstone Way","city":"Northampton","postcode":"NN2 8NT","country":"United Kingdom"}
11 | ];
12 |
13 |
14 | const card = [
15 | {"_id":ObjectID("579f21ae98684924944651be"),"_class":"works.weave.socks.users.entities.Card","longNum":"8575776807334952","expires":"08/19","ccv":"014"},
16 | {"_id":ObjectID("579f21ae98684924944651c1"),"_class":"works.weave.socks.users.entities.Card","longNum":"8918468841895184","expires":"08/19","ccv":"597"},
17 | {"_id":ObjectID("579f21ae98684924944651c4"),"_class":"works.weave.socks.users.entities.Card","longNum":"6426429851404909","expires":"08/19","ccv":"381"}
18 | ];
19 |
20 | const cart = [
21 | {"_id":ObjectID("579f21de98689ebf2bf1cd2f"),"_class":"works.weave.socks.cart.entities.Cart","customerId":"579f21ae98684924944651bf","items":[{"$ref":"item","$id":ObjectID("579f227698689ebf2bf1cd31")},{"$ref":"item","$id":ObjectID("579f22ac98689ebf2bf1cd32")}]},
22 | {"_id":ObjectID("579f21e298689ebf2bf1cd30"),"_class":"works.weave.socks.cart.entities.Cart","customerId":"579f21ae98684924944651bfaa","items":[]}
23 | ];
24 |
25 |
26 | const item = [
27 | {"_id":ObjectID("579f227698689ebf2bf1cd31"),"_class":"works.weave.socks.cart.entities.Item","itemId":"819e1fbf-8b7e-4f6d-811f-693534916a8b","quantity":20,"unitPrice":99.0}
28 | ];
29 |
30 |
31 | const customer = [
32 | {"_id":"579f21ae98684924944651bf","_class":"works.weave.socks.users.entities.Customer","firstName":"Eve","lastName":"Berger","username":"Eve_Berger","addresses":[{"$ref":"address","$id":ObjectID("579f21ae98684924944651bd")}],"cards":[{"$ref":"card","$id":ObjectID("579f21ae98684924944651be")}]
33 | },
34 | {"_id":"579f21ae98684924944651c2","_class":"works.weave.socks.users.entities.Customer","firstName":"User","lastName":"Name","username":"user","addresses":[{"$ref":"address","$id":ObjectID("579f21ae98684924944651c0")}],"cards":[{"$ref":"card","$id":ObjectID("579f21ae98684924944651c1")}]},
35 | {"_id":"579f21ae98684924944651c5","_class":"works.weave.socks.users.entities.Customer","firstName":"User1","lastName":"Name1","username":"user1","addresses":[{"$ref":"address","$id":ObjectID("579f21ae98684924944651c3")}],"cards":[{"$ref":"card","$id":ObjectID("579f21ae98684924944651c4")}]}
36 | ];
37 |
38 |
39 | // Setup database connection before Dredd starts testing
40 | hooks.beforeAll((transactions, done) => {
41 | var MongoEndpoint = process.env.MONGO_ENDPOINT || 'mongodb://localhost:32769/data';
42 | MongoClient.connect(MongoEndpoint, function(err, conn) {
43 | if (err) {
44 | console.error(err);
45 | }
46 | db = conn;
47 | done(err);
48 | });
49 | });
50 |
51 | hooks.beforeEach((transaction, done) => {
52 | db.dropDatabase(function (err, res) {
53 | var promisesToKeep = [
54 | db.collection('customer').insertMany(customer),
55 | db.collection('card').insertMany(card),
56 | db.collection('cart').insertMany(cart),
57 | db.collection('address').insertMany(address),
58 | db.collection('item').insertMany(item)
59 | ];
60 | Promise.all(promisesToKeep).then(function(vls) {
61 | done();
62 | }, function(vls) {
63 | done();
64 | });
65 | })
66 |
67 | });
68 |
69 |
70 | hooks.before("/carts/{customerId}/items > POST", function(transaction, done) {
71 | transaction.request.headers['Content-Type'] = 'application/json';
72 | transaction.request.body = JSON.stringify(
73 | {
74 | "itemId":"819e1fbf-8b7e-4f6d-811f-693534916a8b",
75 | "quantity": 20,
76 | "unitPrice" : 99.0
77 | }
78 | );
79 |
80 | done();
81 | });
82 |
83 | // TODO: Can't make POST and PUT work, skipping for now
84 |
85 | // hooks.before("/carts/{customerId}/items > POST", function(transaction, done) {
86 | // transaction.skip = true;
87 | // done();
88 | // });
89 |
90 | hooks.before("/carts/{customerId}/items > PATCH", function(transaction, done) {
91 | transaction.skip = true;
92 | done();
93 | });
94 |
--------------------------------------------------------------------------------
/api-spec/mock.json:
--------------------------------------------------------------------------------
1 | {
2 | "carts": [
3 | {
4 | "id": "57a98d98e4b00679b4a830af"
5 | }
6 | ],
7 | "items": [
8 | {
9 | "id": 1,
10 | "quantity": 10,
11 | "unitPrice": 1.99,
12 | "cartsId": "57a98d98e4b00679b4a830af"
13 | }
14 | ]
15 | }
16 |
--------------------------------------------------------------------------------
/api-spec/routes.json:
--------------------------------------------------------------------------------
1 | {
2 | "/carts/:cartsId/items/:id": "/items/:id"
3 | }
4 |
--------------------------------------------------------------------------------
/docker-compose-zipkin.yml:
--------------------------------------------------------------------------------
1 | version: '2'
2 |
3 | services:
4 | carts:
5 | image: weaveworksdemos/carts
6 | hostname: carts
7 | restart: always
8 | cap_drop:
9 | - all
10 | cap_add:
11 | - NET_BIND_SERVICE
12 | read_only: true
13 | tmpfs:
14 | - /tmp:rw,noexec,nosuid
15 | environment:
16 | - reschedule=on-node-failure
17 | - ZIPKIN_HOST=zipkin
18 | - ZIPKIN_ENABLED=true
19 | ports:
20 | - "8081:80"
21 | zipkin:
22 | image: openzipkin/zipkin
23 | hostname: zipkin
24 | restart: always
25 | cap_drop:
26 | - all
27 | cap_add:
28 | - CHOWN
29 | - SETGID
30 | - SETUID
31 | read_only: true
32 | tmpfs:
33 | - /tmp:rw,noexec,nosuid
34 | environment:
35 | - reschedule=on-node-failure
36 | ports:
37 | - "9411:9411"
38 |
--------------------------------------------------------------------------------
/docker/carts/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM weaveworksdemos/msd-java:jre-latest
2 |
3 | WORKDIR /usr/src/app
4 | COPY *.jar ./app.jar
5 |
6 | RUN chown -R ${SERVICE_USER}:${SERVICE_GROUP} ./app.jar
7 |
8 | USER ${SERVICE_USER}
9 |
10 | ARG BUILD_DATE
11 | ARG BUILD_VERSION
12 | ARG COMMIT
13 |
14 | LABEL org.label-schema.vendor="Weaveworks" \
15 | org.label-schema.build-date="${BUILD_DATE}" \
16 | org.label-schema.version="${BUILD_VERSION}" \
17 | org.label-schema.name="Socks Shop: Cart" \
18 | org.label-schema.description="REST API for Cart service" \
19 | org.label-schema.url="https://github.com/microservices-demo/carts" \
20 | org.label-schema.vcs-url="github.com:microservices-demo/carts.git" \
21 | org.label-schema.vcs-ref="${COMMIT}" \
22 | org.label-schema.schema-version="1.0"
23 |
24 | ENTRYPOINT ["/usr/local/bin/java.sh","-jar","./app.jar", "--port=80"]
25 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | works.weave.microservices-demo
8 | carts
9 | jar
10 |
11 | carts
12 | Carts service for microservices-demo application
13 |
14 |
15 | org.springframework.boot
16 | spring-boot-starter-parent
17 | 2.0.4.RELEASE
18 |
19 |
20 |
21 | UTF-8
22 | 1.8
23 | 0.0.21
24 |
25 |
26 |
27 |
28 | org.springframework.boot
29 | spring-boot-starter-data-rest
30 |
31 |
32 | org.springframework.boot
33 | spring-boot-starter-data-mongodb
34 |
35 |
36 | org.springframework.cloud
37 | spring-cloud-starter-zipkin
38 | 1.1.0.RELEASE
39 |
40 |
41 | io.prometheus
42 | simpleclient_spring_boot
43 | ${prometheus.version}
44 |
45 |
46 | io.prometheus
47 | simpleclient_hotspot
48 | ${prometheus.version}
49 |
50 |
51 | io.prometheus
52 | simpleclient_servlet
53 | ${prometheus.version}
54 |
55 |
56 | org.springframework.data
57 | spring-data-rest-hal-browser
58 |
59 |
60 | org.springframework.boot
61 | spring-boot-starter-test
62 | test
63 |
64 |
65 | de.flapdoodle.embed
66 | de.flapdoodle.embed.mongo
67 | 1.50.5
68 | test
69 |
70 |
71 | com.openpojo
72 | openpojo
73 | 0.8.4
74 | test
75 |
76 |
77 |
78 |
79 | carts
80 |
81 |
82 | org.springframework.boot
83 | spring-boot-maven-plugin
84 |
85 |
86 | org.apache.maven.plugins
87 | maven-surefire-plugin
88 | 2.19.1
89 |
90 |
91 | **/Unit*.java
92 |
93 |
94 | **/IT*.java
95 |
96 |
97 |
98 |
99 | org.apache.maven.plugins
100 | maven-failsafe-plugin
101 | 2.18.1
102 |
103 |
104 | **/IT*.java
105 |
106 |
107 | **/Unit*.java
108 |
109 |
110 |
111 |
112 |
113 | integration-test
114 | verify
115 |
116 |
117 |
118 |
119 |
120 | org.jacoco
121 | jacoco-maven-plugin
122 | 0.7.6.201602180812
123 |
124 |
125 | prepare-agent
126 |
127 | prepare-agent
128 |
129 |
130 |
131 |
132 |
133 | org.eluder.coveralls
134 | coveralls-maven-plugin
135 | 4.2.0
136 |
137 |
138 |
139 |
140 |
141 |
142 | spring-releases
143 | https://repo.spring.io/libs-release
144 |
145 |
146 |
147 |
148 | spring-releases
149 | https://repo.spring.io/libs-release
150 |
151 |
152 |
153 |
--------------------------------------------------------------------------------
/scripts/build.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | set -ev
4 |
5 | export BUILD_VERSION="0.0.2-SNAPSHOT"
6 | export BUILD_DATE=`date +%Y-%m-%dT%T%z`
7 |
8 | SCRIPT_DIR=$(dirname "$0")
9 |
10 | if [[ -z "$GROUP" ]] ; then
11 | echo "Cannot find GROUP env var"
12 | exit 1
13 | fi
14 |
15 | if [[ -z "$COMMIT" ]] ; then
16 | echo "Cannot find COMMIT env var"
17 | exit 1
18 | fi
19 |
20 | if [[ "$(uname)" == "Darwin" ]]; then
21 | DOCKER_CMD=docker
22 | else
23 | DOCKER_CMD="sudo docker"
24 | fi
25 | CODE_DIR=$(cd $SCRIPT_DIR/..; pwd)
26 | echo $CODE_DIR
27 | $DOCKER_CMD run --rm -v $HOME/.m2:/root/.m2 -v $CODE_DIR:/usr/src/mymaven -w /usr/src/mymaven maven:3.6-jdk-11 mvn -q -DskipTests package
28 |
29 | cp $CODE_DIR/target/*.jar $CODE_DIR/docker/carts
30 |
31 | for m in ./docker/*/; do
32 | REPO=${GROUP}/$(basename $m)
33 | $DOCKER_CMD build \
34 | --build-arg BUILD_VERSION=$BUILD_VERSION \
35 | --build-arg BUILD_DATE=$BUILD_DATE \
36 | --build-arg COMMIT=$COMMIT \
37 | -t ${REPO}:${COMMIT} $CODE_DIR/$m;
38 | done;
39 |
--------------------------------------------------------------------------------
/scripts/push.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | set -ev
4 |
5 | if [[ -z "$GROUP" ]] ; then
6 | echo "Cannot find GROUP env var"
7 | exit 1
8 | fi
9 |
10 | if [[ -z "$COMMIT" ]] ; then
11 | echo "Cannot find COMMIT env var"
12 | exit 1
13 | fi
14 |
15 | push() {
16 | DOCKER_PUSH=1;
17 | while [ $DOCKER_PUSH -gt 0 ] ; do
18 | echo "Pushing $1";
19 | docker push $1;
20 | DOCKER_PUSH=$(echo $?);
21 | if [[ "$DOCKER_PUSH" -gt 0 ]] ; then
22 | echo "Docker push failed with exit code $DOCKER_PUSH";
23 | fi;
24 | done;
25 | }
26 |
27 | tag_and_push_all() {
28 | if [[ -z "$1" ]] ; then
29 | echo "Please pass the tag"
30 | exit 1
31 | else
32 | TAG=$1
33 | fi
34 | for m in ./docker/*/; do
35 | REPO=${GROUP}/$(basename $m)
36 | if [[ "$COMMIT" != "$TAG" ]]; then
37 | docker tag ${REPO}:${COMMIT} ${REPO}:${TAG}
38 | fi
39 | push "$REPO:$TAG";
40 | done;
41 | }
42 |
43 | # Push snapshot when in master
44 | if [ "$TRAVIS_BRANCH" == "master" ] && [ "$TRAVIS_PULL_REQUEST" == "false" ]; then
45 | tag_and_push_all master-${COMMIT:0:8}
46 | fi;
47 |
48 | # Push tag and latest when tagged
49 | if [ -n "$TRAVIS_TAG" ]; then
50 | tag_and_push_all ${TRAVIS_TAG}
51 | tag_and_push_all latest
52 | fi;
53 |
--------------------------------------------------------------------------------
/src/main/java/works/weave/socks/cart/CartApplication.java:
--------------------------------------------------------------------------------
1 | package works.weave.socks.cart;
2 |
3 | import io.prometheus.client.spring.boot.EnablePrometheusEndpoint;
4 | import org.springframework.boot.SpringApplication;
5 | import org.springframework.boot.autoconfigure.SpringBootApplication;
6 |
7 | @SpringBootApplication
8 | @EnablePrometheusEndpoint
9 | public class CartApplication {
10 | public static void main(String[] args) {
11 | SpringApplication.run(CartApplication.class, args);
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/main/java/works/weave/socks/cart/action/FirstResultOrDefault.java:
--------------------------------------------------------------------------------
1 | package works.weave.socks.cart.action;
2 |
3 | import java.util.Collection;
4 | import java.util.function.Supplier;
5 |
6 | public class FirstResultOrDefault implements Supplier {
7 | private final Collection collection;
8 | private final Supplier nonePresent;
9 |
10 | public FirstResultOrDefault(final Collection collection, final Supplier nonePresent) {
11 | this.collection = collection;
12 | this.nonePresent = nonePresent;
13 | }
14 |
15 | @Override
16 | public T get() {
17 | return collection.stream().findFirst().orElseGet(nonePresent);
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/main/java/works/weave/socks/cart/cart/CartContentsResource.java:
--------------------------------------------------------------------------------
1 | package works.weave.socks.cart.cart;
2 |
3 | import org.slf4j.Logger;
4 | import works.weave.socks.cart.entities.Cart;
5 | import works.weave.socks.cart.entities.Item;
6 |
7 | import java.util.List;
8 | import java.util.function.Supplier;
9 |
10 | import static org.slf4j.LoggerFactory.getLogger;
11 |
12 | public class CartContentsResource implements Contents- {
13 | private final Logger LOG = getLogger(getClass());
14 |
15 | private final CartDAO cartRepository;
16 | private final Supplier> parent;
17 |
18 | public CartContentsResource(CartDAO cartRepository, Supplier> parent) {
19 | this.cartRepository = cartRepository;
20 | this.parent = parent;
21 | }
22 |
23 | @Override
24 | public Supplier
> contents() {
25 | return () -> parentCart().contents();
26 | }
27 |
28 | @Override
29 | public Runnable add(Supplier- item) {
30 | return () -> {
31 | LOG.debug("Adding for user: " + parent.get().value().get().toString() + ", " + item.get());
32 | cartRepository.save(parentCart().add(item.get()));
33 | };
34 | }
35 |
36 | @Override
37 | public Runnable delete(Supplier
- item) {
38 | return () -> {
39 | LOG.debug("Deleting for user: " + parent.get().value().get().toString() + ", " + item.get());
40 | cartRepository.save(parentCart().remove(item.get()));
41 | };
42 | }
43 |
44 | private Cart parentCart() {
45 | return parent.get().value().get();
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/main/java/works/weave/socks/cart/cart/CartDAO.java:
--------------------------------------------------------------------------------
1 | package works.weave.socks.cart.cart;
2 |
3 | import works.weave.socks.cart.entities.Cart;
4 |
5 | import java.util.Collections;
6 | import java.util.HashMap;
7 | import java.util.List;
8 | import java.util.Map;
9 |
10 | public interface CartDAO {
11 | void delete(Cart cart);
12 |
13 | Cart save(Cart cart);
14 |
15 | List findByCustomerId(String customerId);
16 |
17 | class Fake implements CartDAO {
18 | private Map cartStore = new HashMap<>();
19 |
20 | @Override
21 | public void delete(Cart cart) {
22 | cartStore.remove(cart.customerId);
23 | }
24 |
25 | @Override
26 | public Cart save(Cart cart) {
27 | return cartStore.put(cart.customerId, cart);
28 | }
29 |
30 | @Override
31 | public List findByCustomerId(String customerId) {
32 | Cart cart = cartStore.get(customerId);
33 | if (cart != null) {
34 | return Collections.singletonList(cart);
35 | } else {
36 | return Collections.emptyList();
37 | }
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/main/java/works/weave/socks/cart/cart/CartResource.java:
--------------------------------------------------------------------------------
1 | package works.weave.socks.cart.cart;
2 |
3 | import works.weave.socks.cart.action.FirstResultOrDefault;
4 | import works.weave.socks.cart.entities.Cart;
5 |
6 | import java.util.function.Supplier;
7 |
8 | public class CartResource implements Resource, HasContents {
9 | private final CartDAO cartRepository;
10 | private final String customerId;
11 |
12 | public CartResource(CartDAO cartRepository, String customerId) {
13 | this.cartRepository = cartRepository;
14 | this.customerId = customerId;
15 | }
16 |
17 | @Override
18 | public Runnable destroy() {
19 | return () -> cartRepository.delete(value().get());
20 | }
21 |
22 | @Override
23 | public Supplier create() {
24 | return () -> cartRepository.save(new Cart(customerId));
25 | }
26 |
27 | @Override
28 | public Supplier value() {
29 | return new FirstResultOrDefault<>(
30 | cartRepository.findByCustomerId(customerId),
31 | () -> {
32 | create().get();
33 | return value().get();
34 | });
35 | }
36 |
37 | @Override
38 | public Runnable merge(Cart toMerge) {
39 | return () -> toMerge.contents().forEach(item -> contents().get().add(() -> item).run());
40 | }
41 |
42 | @Override
43 | public Supplier contents() {
44 | return () -> new CartContentsResource(cartRepository, () -> this);
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/src/main/java/works/weave/socks/cart/cart/Contents.java:
--------------------------------------------------------------------------------
1 | package works.weave.socks.cart.cart;
2 |
3 | import works.weave.socks.cart.entities.Item;
4 |
5 | import java.util.List;
6 | import java.util.function.Supplier;
7 |
8 | public interface Contents {
9 | Supplier
> contents();
10 |
11 | Runnable add(Supplier- item);
12 |
13 | Runnable delete(Supplier
- item);
14 | }
15 |
--------------------------------------------------------------------------------
/src/main/java/works/weave/socks/cart/cart/HasContents.java:
--------------------------------------------------------------------------------
1 | package works.weave.socks.cart.cart;
2 |
3 | import java.util.function.Supplier;
4 |
5 | public interface HasContents {
6 | Supplier contents();
7 | }
8 |
--------------------------------------------------------------------------------
/src/main/java/works/weave/socks/cart/cart/Resource.java:
--------------------------------------------------------------------------------
1 | package works.weave.socks.cart.cart;
2 |
3 | import works.weave.socks.cart.entities.Cart;
4 |
5 | import java.util.function.Supplier;
6 |
7 | public interface Resource {
8 | Runnable destroy();
9 |
10 | Supplier create();
11 |
12 | Supplier value();
13 |
14 | Runnable merge(T toMerge);
15 |
16 | class CartFake implements Resource {
17 | private final String customerId;
18 | private Cart cart = null;
19 |
20 | public CartFake(String customerId) {
21 | this.customerId = customerId;
22 | }
23 |
24 | @Override
25 | public Runnable destroy() {
26 | return () -> cart = null;
27 | }
28 |
29 | @Override
30 | public Supplier create() {
31 | return () -> cart = new Cart(customerId);
32 | }
33 |
34 | @Override
35 | public Supplier value() {
36 | if (cart == null) {
37 | create().get();
38 | }
39 | return () -> cart;
40 | }
41 |
42 | @Override
43 | public Runnable merge(Cart toMerge) {
44 | return null;
45 | }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/main/java/works/weave/socks/cart/configuration/BeanConfiguration.java:
--------------------------------------------------------------------------------
1 | package works.weave.socks.cart.configuration;
2 |
3 | import org.springframework.beans.factory.annotation.Autowired;
4 | import org.springframework.context.annotation.Bean;
5 | import org.springframework.context.annotation.Configuration;
6 | import works.weave.socks.cart.cart.CartDAO;
7 | import works.weave.socks.cart.entities.Cart;
8 | import works.weave.socks.cart.entities.Item;
9 | import works.weave.socks.cart.item.ItemDAO;
10 | import works.weave.socks.cart.repositories.CartRepository;
11 | import works.weave.socks.cart.repositories.ItemRepository;
12 |
13 | import java.util.List;
14 |
15 | @Configuration
16 | public class BeanConfiguration {
17 | @Bean
18 | @Autowired
19 | public CartDAO getCartDao(CartRepository cartRepository) {
20 | return new CartDAO() {
21 | @Override
22 | public void delete(Cart cart) {
23 | cartRepository.delete(cart);
24 | }
25 |
26 | @Override
27 | public Cart save(Cart cart) {
28 | return cartRepository.save(cart);
29 | }
30 |
31 | @Override
32 | public List findByCustomerId(String customerId) {
33 | return cartRepository.findByCustomerId(customerId);
34 | }
35 | };
36 | }
37 |
38 | @Bean
39 | @Autowired
40 | public ItemDAO getItemDao(ItemRepository itemRepository) {
41 | return new ItemDAO() {
42 | @Override
43 | public Item save(Item item) {
44 | return itemRepository.save(item);
45 | }
46 |
47 | @Override
48 | public void destroy(Item item) {
49 | itemRepository.delete(item);
50 | }
51 |
52 | @Override
53 | public Item findOne(String id) {
54 | return itemRepository.findById(id).orElse(null);
55 | }
56 | };
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/src/main/java/works/weave/socks/cart/configuration/MongoConfiguration.java:
--------------------------------------------------------------------------------
1 | package works.weave.socks.cart.configuration;
2 |
3 | import org.springframework.boot.autoconfigure.AutoConfigureBefore;
4 | import org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration;
5 | import org.springframework.context.annotation.Bean;
6 | import org.springframework.context.annotation.Configuration;
7 |
8 | import com.mongodb.MongoClientOptions;
9 |
10 | @Configuration
11 | @AutoConfigureBefore(MongoAutoConfiguration.class)
12 | public class MongoConfiguration {
13 |
14 | @Bean
15 | public MongoClientOptions optionsProvider() {
16 | MongoClientOptions.Builder optionsBuilder = new MongoClientOptions.Builder();
17 | optionsBuilder.serverSelectionTimeout(10000);
18 | MongoClientOptions options = optionsBuilder.build();
19 | return options;
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/main/java/works/weave/socks/cart/configuration/ValidationConfiguration.java:
--------------------------------------------------------------------------------
1 | package works.weave.socks.cart.configuration;
2 |
3 | import org.springframework.context.annotation.Bean;
4 | import org.springframework.context.annotation.Configuration;
5 | import org.springframework.data.mongodb.core.mapping.event.ValidatingMongoEventListener;
6 | import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
7 |
8 | @Configuration
9 | public class ValidationConfiguration {
10 | @Bean
11 | public ValidatingMongoEventListener validatingMongoEventListener() {
12 | return new ValidatingMongoEventListener(validator());
13 | }
14 |
15 | @Bean
16 | public LocalValidatorFactoryBean validator() {
17 | return new LocalValidatorFactoryBean();
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/main/java/works/weave/socks/cart/configuration/WebMvcConfig.java:
--------------------------------------------------------------------------------
1 | package works.weave.socks.cart.configuration;
2 |
3 | import org.springframework.context.annotation.Bean;
4 | import org.springframework.context.annotation.Configuration;
5 | import org.springframework.web.servlet.handler.MappedInterceptor;
6 | import works.weave.socks.cart.middleware.HTTPMonitoringInterceptor;
7 |
8 | @Configuration
9 | public class WebMvcConfig {
10 | @Bean
11 | HTTPMonitoringInterceptor httpMonitoringInterceptor() {
12 | return new HTTPMonitoringInterceptor();
13 | }
14 |
15 | @Bean
16 | public MappedInterceptor myMappedInterceptor(HTTPMonitoringInterceptor interceptor) {
17 | return new MappedInterceptor(new String[]{"/**"}, interceptor);
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/main/java/works/weave/socks/cart/controllers/CartsController.java:
--------------------------------------------------------------------------------
1 | package works.weave.socks.cart.controllers;
2 |
3 | import org.slf4j.Logger;
4 | import org.slf4j.LoggerFactory;
5 | import org.springframework.beans.factory.annotation.Autowired;
6 | import org.springframework.http.HttpStatus;
7 | import org.springframework.http.MediaType;
8 | import org.springframework.web.bind.annotation.*;
9 | import works.weave.socks.cart.cart.CartDAO;
10 | import works.weave.socks.cart.cart.CartResource;
11 | import works.weave.socks.cart.entities.Cart;
12 |
13 |
14 | @RestController
15 | @RequestMapping(path = "/carts")
16 | public class CartsController {
17 | private final Logger logger = LoggerFactory.getLogger(this.getClass());
18 |
19 | @Autowired
20 | private CartDAO cartDAO;
21 |
22 | @ResponseStatus(HttpStatus.OK)
23 | @RequestMapping(value = "/{customerId}", produces = MediaType.APPLICATION_JSON_VALUE, method = RequestMethod.GET)
24 | public Cart get(@PathVariable String customerId) {
25 | return new CartResource(cartDAO, customerId).value().get();
26 | }
27 |
28 | @ResponseStatus(HttpStatus.ACCEPTED)
29 | @RequestMapping(value = "/{customerId}", method = RequestMethod.DELETE)
30 | public void delete(@PathVariable String customerId) {
31 | new CartResource(cartDAO, customerId).destroy().run();
32 | }
33 |
34 | @ResponseStatus(HttpStatus.ACCEPTED)
35 | @RequestMapping(value = "/{customerId}/merge", method = RequestMethod.GET)
36 | public void mergeCarts(@PathVariable String customerId, @RequestParam(value = "sessionId") String sessionId) {
37 | logger.debug("Merge carts request received for ids: " + customerId + " and " + sessionId);
38 | CartResource sessionCart = new CartResource(cartDAO, sessionId);
39 | CartResource customerCart = new CartResource(cartDAO, customerId);
40 | customerCart.merge(sessionCart.value().get()).run();
41 | delete(sessionId);
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/main/java/works/weave/socks/cart/controllers/HealthCheckController.java:
--------------------------------------------------------------------------------
1 | package works.weave.socks.cart.controllers;
2 |
3 | import org.springframework.beans.factory.annotation.Autowired;
4 | import org.springframework.data.mongodb.core.MongoTemplate;
5 | import org.springframework.http.HttpStatus;
6 | import org.springframework.web.bind.annotation.*;
7 | import works.weave.socks.cart.entities.HealthCheck;
8 |
9 | import java.util.ArrayList;
10 | import java.util.Calendar;
11 | import java.util.Date;
12 | import java.util.HashMap;
13 | import java.util.List;
14 | import java.util.Map;
15 |
16 | @RestController
17 | public class HealthCheckController {
18 |
19 | @Autowired
20 | private MongoTemplate mongoTemplate;
21 |
22 | @ResponseStatus(HttpStatus.OK)
23 | @RequestMapping(method = RequestMethod.GET, path = "/health")
24 | public
25 | @ResponseBody
26 | Map> getHealth() {
27 | Map> map = new HashMap>();
28 | List healthChecks = new ArrayList();
29 | Date dateNow = Calendar.getInstance().getTime();
30 |
31 | HealthCheck app = new HealthCheck("carts", "OK", dateNow);
32 | HealthCheck database = new HealthCheck("carts-db", "OK", dateNow);
33 |
34 | try {
35 | mongoTemplate.executeCommand("{ buildInfo: 1 }");
36 | } catch (Exception e) {
37 | database.setStatus("err");
38 | }
39 |
40 | healthChecks.add(app);
41 | healthChecks.add(database);
42 |
43 | map.put("health", healthChecks);
44 | return map;
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/src/main/java/works/weave/socks/cart/controllers/ItemsController.java:
--------------------------------------------------------------------------------
1 | package works.weave.socks.cart.controllers;
2 |
3 | import org.slf4j.Logger;
4 | import org.springframework.beans.factory.annotation.Autowired;
5 | import org.springframework.http.HttpStatus;
6 | import org.springframework.http.MediaType;
7 | import org.springframework.web.bind.annotation.*;
8 | import works.weave.socks.cart.cart.CartDAO;
9 | import works.weave.socks.cart.cart.CartResource;
10 | import works.weave.socks.cart.entities.Item;
11 | import works.weave.socks.cart.item.FoundItem;
12 | import works.weave.socks.cart.item.ItemDAO;
13 | import works.weave.socks.cart.item.ItemResource;
14 |
15 | import java.util.List;
16 | import java.util.function.Supplier;
17 |
18 | import static org.slf4j.LoggerFactory.getLogger;
19 |
20 | @RestController
21 | @RequestMapping(value = "/carts/{customerId:.*}/items")
22 | public class ItemsController {
23 | private final Logger LOG = getLogger(getClass());
24 |
25 | @Autowired
26 | private ItemDAO itemDAO;
27 | @Autowired
28 | private CartsController cartsController;
29 | @Autowired
30 | private CartDAO cartDAO;
31 |
32 | @ResponseStatus(HttpStatus.OK)
33 | @RequestMapping(value = "/{itemId:.*}", produces = MediaType.APPLICATION_JSON_VALUE, method = RequestMethod.GET)
34 | public Item get(@PathVariable String customerId, @PathVariable String itemId) {
35 | return new FoundItem(() -> getItems(customerId), () -> new Item(itemId)).get();
36 | }
37 |
38 | @ResponseStatus(HttpStatus.OK)
39 | @RequestMapping(produces = MediaType.APPLICATION_JSON_VALUE, method = RequestMethod.GET)
40 | public List
- getItems(@PathVariable String customerId) {
41 | return cartsController.get(customerId).contents();
42 | }
43 |
44 | @ResponseStatus(HttpStatus.CREATED)
45 | @RequestMapping(consumes = MediaType.APPLICATION_JSON_VALUE, method = RequestMethod.POST)
46 | public Item addToCart(@PathVariable String customerId, @RequestBody Item item) {
47 | // If the item does not exist in the cart, create new one in the repository.
48 | FoundItem foundItem = new FoundItem(() -> cartsController.get(customerId).contents(), () -> item);
49 | if (!foundItem.hasItem()) {
50 | Supplier
- newItem = new ItemResource(itemDAO, () -> item).create();
51 | LOG.debug("Did not find item. Creating item for user: " + customerId + ", " + newItem.get());
52 | new CartResource(cartDAO, customerId).contents().get().add(newItem).run();
53 | return item;
54 | } else {
55 | Item newItem = new Item(foundItem.get(), foundItem.get().quantity() + 1);
56 | LOG.debug("Found item in cart. Incrementing for user: " + customerId + ", " + newItem);
57 | updateItem(customerId, newItem);
58 | return newItem;
59 | }
60 | }
61 |
62 | @ResponseStatus(HttpStatus.ACCEPTED)
63 | @RequestMapping(value = "/{itemId:.*}", method = RequestMethod.DELETE)
64 | public void removeItem(@PathVariable String customerId, @PathVariable String itemId) {
65 | FoundItem foundItem = new FoundItem(() -> getItems(customerId), () -> new Item(itemId));
66 | Item item = foundItem.get();
67 |
68 | LOG.debug("Removing item from cart: " + item);
69 | new CartResource(cartDAO, customerId).contents().get().delete(() -> item).run();
70 |
71 | LOG.debug("Removing item from repository: " + item);
72 | new ItemResource(itemDAO, () -> item).destroy().run();
73 | }
74 |
75 | @ResponseStatus(HttpStatus.ACCEPTED)
76 | @RequestMapping(consumes = MediaType.APPLICATION_JSON_VALUE, method = RequestMethod.PATCH)
77 | public void updateItem(@PathVariable String customerId, @RequestBody Item item) {
78 | // Merge old and new items
79 | ItemResource itemResource = new ItemResource(itemDAO, () -> get(customerId, item.itemId()));
80 | LOG.debug("Merging item in cart for user: " + customerId + ", " + item);
81 | itemResource.merge(item).run();
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/src/main/java/works/weave/socks/cart/entities/Cart.java:
--------------------------------------------------------------------------------
1 | package works.weave.socks.cart.entities;
2 |
3 | import org.springframework.data.annotation.Id;
4 | import org.springframework.data.mongodb.core.mapping.DBRef;
5 | import org.springframework.data.mongodb.core.mapping.Document;
6 |
7 | import javax.validation.constraints.NotNull;
8 | import java.util.ArrayList;
9 | import java.util.List;
10 |
11 | @Document
12 | public class Cart {
13 | @NotNull
14 | public String customerId; // Public instead of getters/setters.
15 | @Id
16 | private String id;
17 | @DBRef
18 | private List
- items = new ArrayList<>();
19 |
20 | public Cart(String customerId) {
21 | this.customerId = customerId;
22 | }
23 |
24 | public Cart() {
25 | this(null);
26 | }
27 |
28 | public List
- contents() {
29 | return items;
30 | }
31 |
32 | public Cart add(Item item) {
33 | items.add(item);
34 | return this;
35 | }
36 |
37 | public Cart remove(Item item) {
38 | items.remove(item);
39 | return this;
40 | }
41 |
42 | @Override
43 | public String toString() {
44 | return "Cart{" +
45 | "id='" + id + '\'' +
46 | ", customerId='" + customerId + '\'' +
47 | ", items=" + items +
48 | '}';
49 | }
50 |
51 | @Override
52 | public boolean equals(Object o) {
53 | if (this == o) return true;
54 | if (o == null || getClass() != o.getClass()) return false;
55 |
56 | Cart cart = (Cart) o;
57 |
58 | if (customerId != null ? !customerId.equals(cart.customerId) : cart.customerId != null) return false;
59 | if (id != null ? !id.equals(cart.id) : cart.id != null) return false;
60 |
61 | return true;
62 | }
63 |
64 | @Override
65 | public int hashCode() {
66 | int result = customerId != null ? customerId.hashCode() : 0;
67 | result = 31 * result + (id != null ? id.hashCode() : 0);
68 | return result;
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/src/main/java/works/weave/socks/cart/entities/HealthCheck.java:
--------------------------------------------------------------------------------
1 | package works.weave.socks.cart.entities;
2 |
3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
4 | import com.fasterxml.jackson.annotation.JsonFormat;
5 |
6 | import java.util.Calendar;
7 | import java.util.Date;
8 |
9 | @JsonIgnoreProperties(ignoreUnknown = true)
10 | public class HealthCheck {
11 | private String service;
12 | private String status;
13 |
14 | @JsonFormat(pattern="yyyy-MM-dd'T'HH:mm:ss.SSSXXX")
15 | private Date date = Calendar.getInstance().getTime();
16 |
17 | public HealthCheck() {
18 |
19 | }
20 |
21 | public HealthCheck(String service, String status, Date date) {
22 | this.service = service;
23 | this.status = status;
24 | this.date = date;
25 | }
26 |
27 | @Override
28 | public String toString() {
29 | return "HealthCheck{" +
30 | "service='" + service + '\'' +
31 | ", status='" + status + '\'' +
32 | ", date='" + date +
33 | '}';
34 | }
35 |
36 | public String getService() {
37 | return service;
38 | }
39 |
40 | public void setService(String service) {
41 | this.service = service;
42 | }
43 |
44 | public String getStatus() {
45 | return status;
46 | }
47 |
48 | public void setStatus(String status) {
49 | this.status = status;
50 | }
51 |
52 | public Date getDate() {
53 | return date;
54 | }
55 |
56 | public void setDate(Date date) {
57 | this.date = date;
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/src/main/java/works/weave/socks/cart/entities/Item.java:
--------------------------------------------------------------------------------
1 | package works.weave.socks.cart.entities;
2 |
3 | import org.springframework.data.annotation.Id;
4 | import org.springframework.data.mongodb.core.mapping.Document;
5 |
6 | import javax.validation.constraints.NotNull;
7 |
8 | @Document
9 | public class Item {
10 | @Id
11 | private String id;
12 |
13 | @NotNull(message = "Item Id must not be null")
14 | private String itemId;
15 | private int quantity;
16 | private float unitPrice;
17 |
18 | public Item(String id, String itemId, int quantity, float unitPrice) {
19 | this.id = id;
20 | this.itemId = itemId;
21 | this.quantity = quantity;
22 | this.unitPrice = unitPrice;
23 | }
24 |
25 | public Item() {
26 | this(null, "", 1, 0F);
27 | }
28 |
29 | public Item(String itemId) {
30 | this(null, itemId, 1, 0F);
31 | }
32 |
33 | public Item(Item item, String id) {
34 | this(id, item.itemId, item.quantity, item.unitPrice);
35 | }
36 |
37 | public Item(Item item, int quantity) {
38 | this(item.id(), item.itemId, quantity, item.unitPrice);
39 | }
40 |
41 | public String id() {
42 | return id;
43 | }
44 |
45 | public String itemId() {
46 | return itemId;
47 | }
48 |
49 | public int quantity() {
50 | return quantity;
51 | }
52 |
53 | @Override
54 | public String toString() {
55 | return "Item{" +
56 | "id='" + id + '\'' +
57 | ", itemId='" + itemId + '\'' +
58 | ", quantity=" + quantity +
59 | ", unitPrice=" + unitPrice +
60 | '}';
61 | }
62 |
63 | @Override
64 | public boolean equals(Object o) {
65 | if (this == o) return true;
66 | if (o == null || getClass() != o.getClass()) return false;
67 |
68 | Item item = (Item) o;
69 |
70 | return itemId != null ? itemId.equals(item.itemId) : item.itemId == null;
71 | }
72 |
73 | // ****** Crappy getter/setters for Jackson JSON invoking ********
74 |
75 | public String getId() {
76 | return id;
77 | }
78 |
79 | public void setId(String id) {
80 | this.id = id;
81 | }
82 |
83 | public String getItemId() {
84 | return itemId;
85 | }
86 |
87 | public void setItemId(String itemId) {
88 | this.itemId = itemId;
89 | }
90 |
91 | public int getQuantity() {
92 | return quantity;
93 | }
94 |
95 | public void setQuantity(int quantity) {
96 | this.quantity = quantity;
97 | }
98 |
99 | public float getUnitPrice() {
100 | return unitPrice;
101 | }
102 |
103 | public void setUnitPrice(float unitPrice) {
104 | this.unitPrice = unitPrice;
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/src/main/java/works/weave/socks/cart/item/FoundItem.java:
--------------------------------------------------------------------------------
1 | package works.weave.socks.cart.item;
2 |
3 | import org.slf4j.Logger;
4 | import works.weave.socks.cart.entities.Item;
5 |
6 | import java.util.List;
7 | import java.util.function.Supplier;
8 |
9 | import static org.slf4j.LoggerFactory.getLogger;
10 |
11 | public class FoundItem implements Supplier
- {
12 | private final Logger LOG = getLogger(getClass());
13 | private final Supplier
> items;
14 | private final Supplier- item;
15 |
16 | public FoundItem(Supplier
> items, Supplier- item) {
17 | this.items = items;
18 | this.item = item;
19 | }
20 |
21 | @Override
22 | public Item get() {
23 | return items.get().stream()
24 | .filter(item.get()::equals)
25 | .findFirst()
26 | .orElseThrow(() -> new IllegalArgumentException("Cannot find item in cart"));
27 | }
28 |
29 | public boolean hasItem() {
30 | boolean present = items.get().stream()
31 | .filter(item.get()::equals)
32 | .findFirst()
33 | .isPresent();
34 | LOG.debug((present ? "Found" : "Didn't find") + " item: " + item.get() + ", in: " + items.get());
35 | return present;
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/main/java/works/weave/socks/cart/item/ItemDAO.java:
--------------------------------------------------------------------------------
1 | package works.weave.socks.cart.item;
2 |
3 | import works.weave.socks.cart.entities.Item;
4 |
5 | import java.util.HashMap;
6 | import java.util.Map;
7 |
8 | public interface ItemDAO {
9 | Item save(Item item);
10 |
11 | void destroy(Item item);
12 |
13 | Item findOne(String id);
14 |
15 | class Fake implements ItemDAO {
16 | private Map store = new HashMap<>();
17 |
18 | @Override
19 | public Item save(Item item) {
20 | return store.put(item.itemId(), item);
21 | }
22 |
23 | @Override
24 | public void destroy(Item item) {
25 | store.remove(item.itemId());
26 |
27 | }
28 |
29 | @Override
30 | public Item findOne(String id) {
31 | return store.entrySet().stream().filter(i -> i.getValue().id().equals(id)).map(Map.Entry::getValue)
32 | .findFirst().orElse(null);
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/main/java/works/weave/socks/cart/item/ItemResource.java:
--------------------------------------------------------------------------------
1 | package works.weave.socks.cart.item;
2 |
3 | import works.weave.socks.cart.cart.Resource;
4 | import works.weave.socks.cart.entities.Item;
5 |
6 | import java.util.function.Supplier;
7 |
8 | public class ItemResource implements Resource
- {
9 | private final ItemDAO itemRepository;
10 | private final Supplier
- item;
11 |
12 | public ItemResource(ItemDAO itemRepository, Supplier
- item) {
13 | this.itemRepository = itemRepository;
14 | this.item = item;
15 | }
16 |
17 | @Override
18 | public Runnable destroy() {
19 | return () -> itemRepository.destroy(value().get());
20 | }
21 |
22 | @Override
23 | public Supplier
- create() {
24 | return () -> itemRepository.save(item.get());
25 | }
26 |
27 | @Override
28 | public Supplier
- value() {
29 | return item; // Basically a null method. Gets the item from the supplier.
30 | }
31 |
32 | @Override
33 | public Runnable merge(Item toMerge) {
34 | return () -> itemRepository.save(new Item(value().get(), toMerge.quantity()));
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/main/java/works/weave/socks/cart/middleware/HTTPMonitoringInterceptor.java:
--------------------------------------------------------------------------------
1 | package works.weave.socks.cart.middleware;
2 |
3 | import io.prometheus.client.Histogram;
4 | import org.springframework.beans.factory.annotation.Autowired;
5 | import org.springframework.beans.factory.annotation.Value;
6 | import org.springframework.context.ApplicationContext;
7 | import org.springframework.data.rest.core.config.RepositoryRestConfiguration;
8 | import org.springframework.data.rest.core.mapping.ResourceMappings;
9 | import org.springframework.data.rest.webmvc.RepositoryRestHandlerMapping;
10 | import org.springframework.data.rest.webmvc.support.JpaHelper;
11 | import org.springframework.web.servlet.HandlerInterceptor;
12 | import org.springframework.web.servlet.ModelAndView;
13 | import org.springframework.web.servlet.mvc.condition.PatternsRequestCondition;
14 | import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
15 |
16 | import javax.servlet.http.HttpServletRequest;
17 | import javax.servlet.http.HttpServletResponse;
18 | import java.util.HashSet;
19 | import java.util.Set;
20 |
21 | public class HTTPMonitoringInterceptor implements HandlerInterceptor {
22 | static final Histogram requestLatency = Histogram.build()
23 | .name("http_request_duration_seconds")
24 | .help("Request duration in seconds.")
25 | .labelNames("service", "method", "path", "status_code")
26 | .register();
27 |
28 | private static final String startTimeKey = "startTime";
29 | @Autowired
30 | ResourceMappings mappings;
31 | @Autowired
32 | JpaHelper jpaHelper;
33 | @Autowired
34 | RepositoryRestConfiguration repositoryConfiguration;
35 | @Autowired
36 | ApplicationContext applicationContext;
37 | @Autowired
38 | RequestMappingHandlerMapping requestMappingHandlerMapping;
39 | private Set urlPatterns;
40 | @Value("${spring.application.name:orders}")
41 | private String serviceName;
42 |
43 | @Override
44 | public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse
45 | httpServletResponse, Object o) throws Exception {
46 | httpServletRequest.setAttribute(startTimeKey, System.nanoTime());
47 | return true;
48 | }
49 |
50 | @Override
51 | public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse
52 | httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
53 | long start = (long) httpServletRequest.getAttribute(startTimeKey);
54 | long elapsed = System.nanoTime() - start;
55 | double seconds = (double) elapsed / 1000000000.0;
56 | String matchedUrl = getMatchingURLPattern(httpServletRequest);
57 | if (!matchedUrl.equals("")) {
58 | requestLatency.labels(
59 | serviceName,
60 | httpServletRequest.getMethod(),
61 | matchedUrl,
62 | Integer.toString(httpServletResponse.getStatus())
63 | ).observe(seconds);
64 | }
65 | }
66 |
67 | @Override
68 | public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse
69 | httpServletResponse, Object o, Exception e) throws Exception {
70 | }
71 |
72 | private String getMatchingURLPattern(HttpServletRequest httpServletRequest) {
73 | String res = "";
74 | for (PatternsRequestCondition pattern : getUrlPatterns()) {
75 | if (pattern.getMatchingCondition(httpServletRequest) != null &&
76 | !httpServletRequest.getServletPath().equals("/error")) {
77 | res = pattern.getMatchingCondition(httpServletRequest).getPatterns().iterator()
78 | .next();
79 | break;
80 | }
81 | }
82 | return res;
83 | }
84 |
85 | private Set getUrlPatterns() {
86 | if (this.urlPatterns == null) {
87 | this.urlPatterns = new HashSet<>();
88 | requestMappingHandlerMapping.getHandlerMethods().forEach((mapping, handlerMethod) ->
89 | urlPatterns.add(mapping.getPatternsCondition()));
90 | RepositoryRestHandlerMapping repositoryRestHandlerMapping = new
91 | RepositoryRestHandlerMapping(mappings, repositoryConfiguration);
92 | repositoryRestHandlerMapping.setJpaHelper(jpaHelper);
93 | repositoryRestHandlerMapping.setApplicationContext(applicationContext);
94 | repositoryRestHandlerMapping.afterPropertiesSet();
95 | repositoryRestHandlerMapping.getHandlerMethods().forEach((mapping, handlerMethod) ->
96 | urlPatterns.add(mapping.getPatternsCondition()));
97 | }
98 | return this.urlPatterns;
99 | }
100 | }
101 |
--------------------------------------------------------------------------------
/src/main/java/works/weave/socks/cart/repositories/CartRepository.java:
--------------------------------------------------------------------------------
1 | package works.weave.socks.cart.repositories;
2 |
3 | import org.springframework.data.mongodb.repository.MongoRepository;
4 | import org.springframework.data.repository.query.Param;
5 | import org.springframework.data.rest.core.annotation.RepositoryRestResource;
6 | import works.weave.socks.cart.entities.Cart;
7 |
8 | import java.util.List;
9 |
10 | @RepositoryRestResource(exported = false)
11 | public interface CartRepository extends MongoRepository {
12 | List findByCustomerId(@Param("custId") String id);
13 | }
14 |
15 |
--------------------------------------------------------------------------------
/src/main/java/works/weave/socks/cart/repositories/ItemRepository.java:
--------------------------------------------------------------------------------
1 | package works.weave.socks.cart.repositories;
2 |
3 | import org.springframework.data.mongodb.repository.MongoRepository;
4 | import org.springframework.data.rest.core.annotation.RepositoryRestResource;
5 | import works.weave.socks.cart.entities.Item;
6 |
7 | @RepositoryRestResource
8 | public interface ItemRepository extends MongoRepository
- {
9 | }
10 |
11 |
--------------------------------------------------------------------------------
/src/main/resources/application.properties:
--------------------------------------------------------------------------------
1 | server.port=${port:8081}
2 | spring.data.mongodb.uri=mongodb://${db:carts-db}:27017/data
3 | endpoints.health.enabled=false
4 | spring.zipkin.baseUrl=http://${zipkin_host:zipkin}:9411/
5 | spring.zipkin.enabled=${zipkin_enabled:false}
6 | spring.sleuth.sampler.percentage=1.0
7 | spring.application.name=carts
8 | # Disable actuator metrics endpoints
9 | endpoints.metrics.enabled=false
10 | endpoints.prometheus.id=metrics
11 |
--------------------------------------------------------------------------------
/src/test/java/works/weave/socks/cart/action/UnitFirstResultOrDefault.java:
--------------------------------------------------------------------------------
1 | package works.weave.socks.cart.action;
2 |
3 | import org.junit.Test;
4 |
5 | import java.util.Arrays;
6 | import java.util.Collections;
7 |
8 | import static org.hamcrest.core.IsEqual.equalTo;
9 | import static org.junit.Assert.assertThat;
10 |
11 | public class UnitFirstResultOrDefault {
12 | @Test
13 | public void whenEmptyUsesDefault() {
14 | String defaultValue = "test";
15 | FirstResultOrDefault CUT = new FirstResultOrDefault<>(Collections.emptyList(), () -> defaultValue);
16 | assertThat(CUT.get(), equalTo(defaultValue));
17 | }
18 |
19 | @Test
20 | public void whenNotEmptyUseFirst() {
21 | String testValue = "test";
22 | FirstResultOrDefault CUT = new FirstResultOrDefault<>(Arrays.asList(testValue), () -> "nonDefault");
23 | assertThat(CUT.get(), equalTo(testValue));
24 | }
25 |
26 | @Test
27 | public void whenMultipleNotEmptyUseFirst() {
28 | String testValue = "test";
29 | FirstResultOrDefault CUT = new FirstResultOrDefault<>(Arrays.asList(testValue, "test2"), () ->
30 | "nonDefault");
31 | assertThat(CUT.get(), equalTo(testValue));
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/test/java/works/weave/socks/cart/cart/UnitCartContentsResource.java:
--------------------------------------------------------------------------------
1 | package works.weave.socks.cart.cart;
2 |
3 | import org.hamcrest.collection.IsCollectionWithSize;
4 | import org.junit.Test;
5 | import works.weave.socks.cart.entities.Cart;
6 | import works.weave.socks.cart.entities.Item;
7 |
8 | import static org.hamcrest.MatcherAssert.assertThat;
9 | import static org.hamcrest.Matchers.containsInAnyOrder;
10 |
11 | public class UnitCartContentsResource {
12 | private final String customerId = "testId";
13 | private final CartDAO.Fake fakeDAO = new CartDAO.Fake();
14 | private final Resource fakeCartResource = new Resource.CartFake(customerId);
15 |
16 | @Test
17 | public void shouldAddAndReturnContents() {
18 | CartContentsResource contentsResource = new CartContentsResource(fakeDAO, () -> fakeCartResource);
19 | Item item = new Item("testId");
20 | contentsResource.add(() -> item).run();
21 | assertThat(contentsResource.contents().get(), IsCollectionWithSize.hasSize(1));
22 | assertThat(contentsResource.contents().get(), containsInAnyOrder(item));
23 | }
24 |
25 | @Test
26 | public void shouldStartEmpty() {
27 | CartContentsResource contentsResource = new CartContentsResource(fakeDAO, () -> fakeCartResource);
28 | assertThat(contentsResource.contents().get(), IsCollectionWithSize.hasSize(0));
29 | }
30 |
31 | @Test
32 | public void shouldDeleteItemFromCart() {
33 | CartContentsResource contentsResource = new CartContentsResource(fakeDAO, () -> fakeCartResource);
34 | Item item = new Item("testId");
35 | contentsResource.add(() -> item).run();
36 | assertThat(contentsResource.contents().get(), IsCollectionWithSize.hasSize(1));
37 | assertThat(contentsResource.contents().get(), containsInAnyOrder(item));
38 | Item item2 = new Item(item.itemId());
39 | contentsResource.delete(() -> item2).run();
40 | assertThat(contentsResource.contents().get(), IsCollectionWithSize.hasSize(0));
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/test/java/works/weave/socks/cart/cart/UnitCartResource.java:
--------------------------------------------------------------------------------
1 | package works.weave.socks.cart.cart;
2 |
3 | import org.junit.Test;
4 | import works.weave.socks.cart.entities.Cart;
5 | import works.weave.socks.cart.entities.Item;
6 |
7 | import static org.hamcrest.CoreMatchers.is;
8 | import static org.hamcrest.CoreMatchers.not;
9 | import static org.hamcrest.CoreMatchers.notNullValue;
10 | import static org.hamcrest.Matchers.anyOf;
11 | import static org.hamcrest.Matchers.*;
12 | import static org.hamcrest.core.IsEqual.equalTo;
13 | import static org.junit.Assert.assertThat;
14 |
15 | public class UnitCartResource {
16 |
17 | private final String customerId = "testId";
18 | private final CartDAO.Fake fake = new CartDAO.Fake();
19 |
20 | @Test
21 | public void whenCartExistsUseThat() {
22 | Cart cart = new Cart(customerId);
23 | fake.save(cart);
24 | CartResource cartResource = new CartResource(fake, customerId);
25 | assertThat(cartResource.value().get(), equalTo(cart));
26 | }
27 |
28 | @Test
29 | public void whenCartDoesntExistCreateNew() {
30 | CartResource cartResource = new CartResource(fake, customerId);
31 | assertThat(cartResource.value().get(), is(notNullValue()));
32 | assertThat(cartResource.value().get().customerId, is(equalTo(customerId)));
33 | }
34 |
35 | @Test
36 | public void whenDestroyRemoveItem() {
37 | Cart cart = new Cart(customerId);
38 | fake.save(cart);
39 | CartResource cartResource = new CartResource(fake, customerId);
40 | cartResource.destroy().run();
41 | assertThat(fake.findByCustomerId(customerId), is(empty()));
42 | }
43 |
44 | @Test
45 | public void whenDestroyOnEmptyStillEmpty() {
46 | CartResource cartResource = new CartResource(fake, customerId);
47 | cartResource.destroy().run();
48 | assertThat(fake.findByCustomerId(customerId), is(empty()));
49 | }
50 |
51 | @Test
52 | public void whenCreateDoCreate() {
53 | CartResource cartResource = new CartResource(fake, customerId);
54 | cartResource.create().get();
55 | assertThat(fake.findByCustomerId(customerId), is(not(empty())));
56 | }
57 |
58 | @Test
59 | public void contentsShouldBeEmptyWhenNew() {
60 | CartResource cartResource = new CartResource(fake, customerId);
61 | cartResource.create().get();
62 | assertThat(cartResource.contents().get().contents().get(), is(empty()));
63 | }
64 |
65 | @Test
66 | public void mergedItemsShouldBeInCart() {
67 | String person1 = "person1";
68 | String person2 = "person2";
69 | Item person1Item = new Item("item1");
70 | Item person2Item = new Item("item2");
71 | CartResource cartResource = new CartResource(fake, person1);
72 | cartResource.contents().get().add(() -> person1Item).run();
73 | CartResource cartResourceToMerge = new CartResource(fake, person2);
74 | cartResourceToMerge.contents().get().add(() -> person2Item).run();
75 | cartResource.merge(cartResourceToMerge.value().get()).run();
76 | assertThat(cartResource.contents().get().contents().get(), hasSize(2));
77 | assertThat(cartResource.contents().get().contents().get().get(0), anyOf(equalTo(person1Item), equalTo
78 | (person2Item)));
79 | assertThat(cartResource.contents().get().contents().get().get(1), anyOf(equalTo(person1Item), equalTo
80 | (person2Item)));
81 | assertThat(cartResourceToMerge.contents().get().contents().get(), hasSize(1));
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/src/test/java/works/weave/socks/cart/controllers/UnitCartsController.java:
--------------------------------------------------------------------------------
1 | package works.weave.socks.cart.controllers;
2 |
3 | import org.junit.Test;
4 | import org.junit.runner.RunWith;
5 | import org.springframework.beans.factory.annotation.Autowired;
6 | import org.springframework.context.annotation.Bean;
7 | import org.springframework.context.annotation.Configuration;
8 | import org.springframework.test.context.ContextConfiguration;
9 | import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
10 | import works.weave.socks.cart.cart.CartDAO;
11 | import works.weave.socks.cart.entities.Cart;
12 | import works.weave.socks.cart.entities.Item;
13 | import works.weave.socks.cart.item.ItemDAO;
14 |
15 | import static org.hamcrest.CoreMatchers.equalTo;
16 | import static org.hamcrest.CoreMatchers.is;
17 | import static org.hamcrest.collection.IsCollectionWithSize.hasSize;
18 | import static org.hamcrest.collection.IsEmptyCollection.empty;
19 | import static org.hamcrest.collection.IsIterableContainingInAnyOrder.containsInAnyOrder;
20 | import static org.junit.Assert.assertThat;
21 |
22 | @RunWith(SpringJUnit4ClassRunner.class)
23 | @ContextConfiguration
24 | public class UnitCartsController {
25 |
26 | @Autowired
27 | private ItemsController itemsController;
28 |
29 | @Autowired
30 | private CartDAO cartDAO;
31 |
32 | @Autowired
33 | private CartsController cartsController;
34 |
35 |
36 | @Test
37 | public void shouldGetCart() {
38 | String customerId = "customerIdGet";
39 | Cart cart = new Cart(customerId);
40 | cartDAO.save(cart);
41 | Cart gotCart = cartsController.get(customerId);
42 | assertThat(gotCart, is(equalTo(cart)));
43 | assertThat(cartDAO.findByCustomerId(customerId).get(0), is(equalTo(cart)));
44 | }
45 |
46 | @Test
47 | public void shouldDeleteCart() {
48 | String customerId = "customerIdGet";
49 | Cart cart = new Cart(customerId);
50 | cartDAO.save(cart);
51 | cartsController.delete(customerId);
52 | assertThat(cartDAO.findByCustomerId(customerId), is(empty()));
53 | }
54 |
55 | @Test
56 | public void shouldMergeItemsInCartsTogether() {
57 | String customerId1 = "customerId1";
58 | Cart cart1 = new Cart(customerId1);
59 | Item itemId1 = new Item("itemId1");
60 | cart1.add(itemId1);
61 | cartDAO.save(cart1);
62 | String customerId2 = "customerId2";
63 | Cart cart2 = new Cart(customerId2);
64 | Item itemId2 = new Item("itemId2");
65 | cart2.add(itemId2);
66 | cartDAO.save(cart2);
67 |
68 | cartsController.mergeCarts(customerId1, customerId2);
69 | assertThat(cartDAO.findByCustomerId(customerId1).get(0).contents(), is(hasSize(2)));
70 | assertThat(cartDAO.findByCustomerId(customerId1).get(0).contents(), is(containsInAnyOrder(itemId1, itemId2)));
71 | assertThat(cartDAO.findByCustomerId(customerId2), is(empty()));
72 | }
73 |
74 | @Configuration
75 | static class ItemsControllerTestConfiguration {
76 | @Bean
77 | public ItemsController itemsController() {
78 | return new ItemsController();
79 | }
80 |
81 | @Bean
82 | public CartsController cartsController() {
83 | return new CartsController();
84 | }
85 |
86 | @Bean
87 | public ItemDAO itemDAO() {
88 | return new ItemDAO.Fake();
89 | }
90 |
91 | @Bean
92 | public CartDAO cartDAO() {
93 | return new CartDAO.Fake();
94 | }
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/src/test/java/works/weave/socks/cart/controllers/UnitHealthCheckController.java:
--------------------------------------------------------------------------------
1 | package works.weave.socks.cart.controllers;
2 |
3 | import org.junit.Test;
4 | import org.junit.runner.RunWith;
5 | import org.springframework.beans.factory.annotation.Autowired;
6 | import org.springframework.context.annotation.Bean;
7 | import org.springframework.context.annotation.Configuration;
8 | import org.springframework.data.mongodb.core.MongoTemplate;
9 | import org.springframework.test.context.ContextConfiguration;
10 | import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
11 | import static org.hamcrest.CoreMatchers.equalTo;
12 | import static org.hamcrest.CoreMatchers.is;
13 | import static org.junit.Assert.assertThat;
14 | import static org.mockito.Mockito.*;
15 | import works.weave.socks.cart.entities.HealthCheck;
16 |
17 |
18 | import java.util.ArrayList;
19 | import java.util.Calendar;
20 | import java.util.Date;
21 | import java.util.HashMap;
22 | import java.util.List;
23 | import java.util.Map;
24 |
25 | @RunWith(SpringJUnit4ClassRunner.class)
26 | @ContextConfiguration
27 | public class UnitHealthCheckController {
28 |
29 | @Autowired
30 | private HealthCheckController healthCheckController;
31 |
32 | @Test
33 | public void shouldGetHealth() {
34 | Map> results = this.healthCheckController.getHealth();
35 | assertThat(results.get("health").size(), is(equalTo(2)));
36 | }
37 |
38 | @Configuration
39 | static class HealthCheckControllerTestConfiguration {
40 | @Bean
41 | public HealthCheckController healthCheckController() {
42 | return new HealthCheckController();
43 | }
44 |
45 | @Bean
46 | public MongoTemplate mongoTemplate() {
47 | MongoTemplate mongoTemplate = mock(MongoTemplate.class);
48 | return mongoTemplate;
49 | }
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/src/test/java/works/weave/socks/cart/controllers/UnitItemsController.java:
--------------------------------------------------------------------------------
1 | package works.weave.socks.cart.controllers;
2 |
3 | import org.junit.Test;
4 | import org.junit.runner.RunWith;
5 | import org.springframework.beans.factory.annotation.Autowired;
6 | import org.springframework.context.annotation.Bean;
7 | import org.springframework.context.annotation.Configuration;
8 | import org.springframework.test.context.ContextConfiguration;
9 | import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
10 | import works.weave.socks.cart.cart.CartDAO;
11 | import works.weave.socks.cart.entities.Item;
12 | import works.weave.socks.cart.item.ItemDAO;
13 |
14 | import static org.hamcrest.CoreMatchers.equalTo;
15 | import static org.hamcrest.CoreMatchers.is;
16 | import static org.hamcrest.collection.IsCollectionWithSize.hasSize;
17 | import static org.junit.Assert.assertThat;
18 |
19 | @RunWith(SpringJUnit4ClassRunner.class)
20 | @ContextConfiguration
21 | public class UnitItemsController {
22 |
23 | @Autowired
24 | private ItemsController itemsController;
25 |
26 | @Autowired
27 | private ItemDAO itemDAO;
28 |
29 | @Autowired
30 | private CartsController cartsController;
31 |
32 | @Test
33 | public void whenNewItemAdd() {
34 | Item item = new Item("id", "itemId", 1, 0F);
35 | String customerId = "customerIdAdd";
36 | itemsController.addToCart(customerId, item);
37 | assertThat(itemsController.getItems(customerId), is(hasSize(1)));
38 | assertThat(itemsController.getItems(customerId), is(org.hamcrest.CoreMatchers.hasItem(item)));
39 | }
40 |
41 | @Test
42 | public void whenExistIncrementQuantity() {
43 | Item item = new Item("id", "itemId", 1, 0F);
44 | String customerId = "customerIdIncrement";
45 | itemsController.addToCart(customerId, item);
46 | itemsController.addToCart(customerId, item);
47 | assertThat(itemsController.getItems(customerId), is(hasSize(1)));
48 | assertThat(itemsController.getItems(customerId), is(org.hamcrest.CoreMatchers.hasItem(item)));
49 | assertThat(itemDAO.findOne(item.id()).quantity(), is(equalTo(2)));
50 | }
51 |
52 | @Test
53 | public void shouldRemoveItemFromCart() {
54 | Item item = new Item("id", "itemId", 1, 0F);
55 | String customerId = "customerIdRemove";
56 | itemsController.addToCart(customerId, item);
57 | assertThat(itemsController.getItems(customerId), is(hasSize(1)));
58 | itemsController.removeItem(customerId, item.itemId());
59 | assertThat(itemsController.getItems(customerId), is(hasSize(0)));
60 | }
61 |
62 | @Test
63 | public void shouldSetQuantity() {
64 | Item item = new Item("id", "itemId", 1, 0F);
65 | String customerId = "customerIdQuantity";
66 | itemsController.addToCart(customerId, item);
67 | assertThat(itemsController.getItems(customerId).get(0).quantity(), is(equalTo(item.quantity())));
68 | Item anotherItem = new Item(item, 15);
69 | itemsController.updateItem(customerId, anotherItem);
70 | assertThat(itemDAO.findOne(item.id()).quantity(), is(equalTo(anotherItem.quantity())));
71 | }
72 |
73 | @Configuration
74 | static class ItemsControllerTestConfiguration {
75 | @Bean
76 | public ItemsController itemsController() {
77 | return new ItemsController();
78 | }
79 |
80 | @Bean
81 | public CartsController cartsController() {
82 | return new CartsController();
83 | }
84 |
85 | @Bean
86 | public ItemDAO itemDAO() {
87 | return new ItemDAO.Fake();
88 | }
89 |
90 | @Bean
91 | public CartDAO cartDAO() {
92 | return new CartDAO.Fake();
93 | }
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/src/test/java/works/weave/socks/cart/item/UnitFoundItem.java:
--------------------------------------------------------------------------------
1 | package works.weave.socks.cart.item;
2 |
3 | import org.junit.Test;
4 | import works.weave.socks.cart.entities.Item;
5 |
6 | import java.util.ArrayList;
7 | import java.util.List;
8 |
9 | import static org.hamcrest.CoreMatchers.is;
10 | import static org.hamcrest.core.IsEqual.equalTo;
11 | import static org.junit.Assert.assertThat;
12 |
13 | public class UnitFoundItem {
14 | @Test
15 | public void findOneItem() {
16 | List
- list = new ArrayList<>();
17 | String testId = "testId";
18 | Item testAnswer = new Item(testId);
19 | list.add(testAnswer);
20 | FoundItem foundItem = new FoundItem(() -> list, () -> testAnswer);
21 | assertThat(foundItem.get(), is(equalTo(testAnswer)));
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/test/java/works/weave/socks/cart/item/UnitItemResource.java:
--------------------------------------------------------------------------------
1 | package works.weave.socks.cart.item;
2 |
3 | import org.junit.Test;
4 | import works.weave.socks.cart.entities.Item;
5 |
6 | import static org.hamcrest.CoreMatchers.is;
7 | import static org.hamcrest.Matchers.nullValue;
8 | import static org.hamcrest.core.IsEqual.equalTo;
9 | import static org.junit.Assert.assertThat;
10 |
11 |
12 | public class UnitItemResource {
13 | private ItemDAO itemDAO = new ItemDAO.Fake();
14 |
15 | @Test
16 | public void testCreateAndDestroy() {
17 | Item item = new Item("itemId", "testId", 1, 0F);
18 | ItemResource itemResource = new ItemResource(itemDAO, () -> item);
19 | itemResource.create().get();
20 | assertThat(itemDAO.findOne(item.id()), is(equalTo(item)));
21 | itemResource.destroy().run();
22 | assertThat(itemDAO.findOne(item.id()), is(nullValue()));
23 | }
24 |
25 | @Test
26 | public void mergedItemShouldHaveNewQuantity() {
27 | Item item = new Item("itemId", "testId", 1, 0F);
28 | ItemResource itemResource = new ItemResource(itemDAO, () -> item);
29 | assertThat(itemResource.value().get(), is(equalTo(item)));
30 | Item newItem = new Item(item, 10);
31 | itemResource.merge(newItem).run();
32 | assertThat(itemDAO.findOne(item.id()).quantity(), is(equalTo(newItem.quantity())));
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/test/java/works/weave/socks/cart/repositories/ITCartRepository.java:
--------------------------------------------------------------------------------
1 | package works.weave.socks.cart.repositories;
2 |
3 | import org.junit.Before;
4 | import org.junit.Test;
5 | import org.junit.runner.RunWith;
6 | import org.springframework.beans.factory.annotation.Autowired;
7 | import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
8 | import org.springframework.test.context.junit4.SpringRunner;
9 | import works.weave.socks.cart.entities.Cart;
10 |
11 | import java.util.List;
12 |
13 | import static org.junit.Assert.assertEquals;
14 |
15 | @RunWith(SpringRunner.class)
16 | @EnableAutoConfiguration
17 | public class ITCartRepository {
18 | @Autowired
19 | private CartRepository cartRepository;
20 |
21 | @Before
22 | public void removeAllData() {
23 | cartRepository.deleteAll();
24 | }
25 |
26 | @Test
27 | public void testCartSave() {
28 | Cart original = new Cart("customerId");
29 | Cart saved = cartRepository.save(original);
30 |
31 | assertEquals(1, cartRepository.count());
32 | assertEquals(original, saved);
33 | }
34 |
35 | @Test
36 | public void testCartGetDefault() {
37 | Cart original = new Cart("customerId");
38 | Cart saved = cartRepository.save(original);
39 |
40 | assertEquals(1, cartRepository.count());
41 | assertEquals(original, saved);
42 | }
43 |
44 | @Test
45 | public void testSearchCustomerById() {
46 | Cart original = new Cart("customerId");
47 | cartRepository.save(original);
48 |
49 | List found = cartRepository.findByCustomerId(original.customerId);
50 | assertEquals(1, found.size());
51 | assertEquals(original, found.get(0));
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/src/test/java/works/weave/socks/cart/repositories/ITItemRepository.java:
--------------------------------------------------------------------------------
1 | package works.weave.socks.cart.repositories;
2 |
3 | import org.junit.Before;
4 | import org.junit.Test;
5 | import org.junit.runner.RunWith;
6 | import org.springframework.beans.factory.annotation.Autowired;
7 | import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
8 | import org.springframework.test.context.junit4.SpringRunner;
9 | import works.weave.socks.cart.entities.Item;
10 |
11 | import static org.junit.Assert.assertEquals;
12 |
13 | @RunWith(SpringRunner.class)
14 | @EnableAutoConfiguration
15 | public class ITItemRepository {
16 | @Autowired
17 | private ItemRepository itemRepository;
18 |
19 | @Before
20 | public void removeAllData() {
21 | itemRepository.deleteAll();
22 | }
23 |
24 | @Test
25 | public void testCustomerSave() {
26 | Item original = new Item("id", "itemId", 1, 0.99F);
27 | Item saved = itemRepository.save(original);
28 |
29 | assertEquals(1, itemRepository.count());
30 | assertEquals(original, saved);
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/test/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM python:3.6-alpine
2 |
3 | RUN apk add --no-cache \
4 | ca-certificates \
5 | curl \
6 | openssl
7 |
8 | ENV DOCKER_BUCKET get.docker.com
9 | ENV DOCKER_VERSION 1.8.3
10 |
11 | RUN set -x \
12 | && curl -fSL "https://${DOCKER_BUCKET}/builds/Linux/x86_64/docker-${DOCKER_VERSION}.tgz" -o docker.tgz \
13 | && tar -xzvf docker.tgz \
14 | && docker -v
15 |
16 | RUN pip install requests
17 |
--------------------------------------------------------------------------------
/test/component.py:
--------------------------------------------------------------------------------
1 | import os
2 | import unittest
3 | from os.path import expanduser
4 |
5 | from util.Docker import Docker
6 |
7 |
8 | class JavaServices(unittest.TestCase):
9 | def test_maven(self):
10 | script_dir = os.path.dirname(os.path.realpath(__file__))
11 | code_dir = script_dir + "/.."
12 | home = expanduser("~")
13 | command = ['docker', 'run', '--rm', '-v', home + '/.m2:/root/.m2', '-v', code_dir + ':/usr/src/mymaven', '-w',
14 | '/usr/src/mymaven', 'maven:3.6-jdk-11', 'mvn', 'integration-test']
15 | print(Docker().execute(command))
16 |
17 |
18 | if __name__ == '__main__':
19 | unittest.main()
20 |
--------------------------------------------------------------------------------
/test/container.py:
--------------------------------------------------------------------------------
1 | import argparse
2 | import sys
3 | import unittest
4 | import os
5 | from util.Api import Api
6 | from time import sleep
7 |
8 | from util.Docker import Docker
9 | from util.Dredd import Dredd
10 |
11 | class CartContainerTest(unittest.TestCase):
12 | TAG = "latest"
13 | COMMIT = ""
14 | container_name = Docker().random_container_name('carts')
15 | mongo_container_name = Docker().random_container_name('carts-db')
16 | def __init__(self, methodName='runTest'):
17 | super(CartContainerTest, self).__init__(methodName)
18 | self.ip = ""
19 |
20 | def setUp(self):
21 | Docker().start_container(container_name=self.mongo_container_name, image="mongo", host="carts-db")
22 | command = ['docker', 'run',
23 | '-d',
24 | '--name', CartContainerTest.container_name,
25 | '-h', 'carts',
26 | '--link',
27 | CartContainerTest.mongo_container_name,
28 | 'weaveworksdemos/carts:' + self.COMMIT]
29 | Docker().execute(command)
30 | self.ip = Docker().get_container_ip(CartContainerTest.container_name)
31 |
32 | def tearDown(self):
33 | Docker().kill_and_remove(CartContainerTest.container_name)
34 | Docker().kill_and_remove(CartContainerTest.mongo_container_name)
35 |
36 | def test_api_validated(self):
37 | limit = 30
38 | while Api().noResponse('http://' + self.ip + ':80/carts/'):
39 | if limit == 0:
40 | self.fail("Couldn't get the API running")
41 | limit = limit - 1
42 | sleep(1)
43 |
44 | out = Dredd().test_against_endpoint(
45 | "carts", "http://carts/",
46 | links=[self.mongo_container_name, self.container_name],
47 | env=[("MONGO_ENDPOINT", "mongodb://carts-db:27017/data")],
48 | dump_streams=True)
49 | self.assertGreater(out.find("0 failing"), -1)
50 | self.assertGreater(out.find("0 errors"), -1)
51 | print(out)
52 |
53 | if __name__ == '__main__':
54 | parser = argparse.ArgumentParser()
55 | default_tag = "latest"
56 | parser.add_argument('--tag', default=default_tag, help='The tag of the image to use. (default: latest)')
57 | parser.add_argument('unittest_args', nargs='*')
58 | args = parser.parse_args()
59 | CartContainerTest.TAG = args.tag
60 |
61 | if CartContainerTest.TAG == "":
62 | CartContainerTest.TAG = default_tag
63 |
64 | CartContainerTest.COMMIT = os.environ["COMMIT"]
65 | # Now set the sys.argv to the unittest_args (leaving sys.argv[0] alone)
66 | sys.argv[1:] = args.unittest_args
67 | unittest.main()
68 |
--------------------------------------------------------------------------------
/test/coveralls.py:
--------------------------------------------------------------------------------
1 | import os
2 | import unittest
3 | from os.path import expanduser
4 |
5 | from util.Docker import Docker
6 |
7 |
8 | class JavaServices(unittest.TestCase):
9 | def test_maven(self):
10 | script_dir = os.path.dirname(os.path.realpath(__file__))
11 | code_dir = script_dir + "/.."
12 | home = expanduser("~")
13 | command = ['docker', 'run', '--rm',
14 | '-v', home + '/.m2:/root/.m2',
15 | '-v', code_dir + ':/usr/src/mymaven',
16 | '-w', '/usr/src/mymaven',
17 | 'maven:3.6-jdk-11',
18 | 'mvn',
19 | '-DrepoToken=' + os.getenv('COVERALLS_TOKEN'),
20 | '-DserviceJobId=' + os.getenv('TRAVIS_JOB_ID'),
21 | '-Dbranch=' + os.getenv('TRAVIS_BRANCH'),
22 | '-DpullRequest=' + os.getenv('TRAVIS_PULL_REQUEST'),
23 | '-DserviceName=' + os.getenv('TRAVIS'),
24 | 'verify',
25 | 'jacoco:report',
26 | 'coveralls:report']
27 | print("Coveralls command: ",
28 | '-DserviceJobId=' + os.getenv('TRAVIS_JOB_ID'),
29 | '-Dbranch=' + os.getenv('TRAVIS_BRANCH'),
30 | '-DpullRequest=' + os.getenv('TRAVIS_PULL_REQUEST'),
31 | '-DserviceName=' + 'TRAVIS')
32 | print(Docker().execute(command))
33 |
34 |
35 | if __name__ == '__main__':
36 | unittest.main()
37 |
--------------------------------------------------------------------------------
/test/test.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | set -ev
4 |
5 | SCRIPT_DIR=`dirname "$0"`
6 | SCRIPT_NAME=`basename "$0"`
7 | SSH_OPTS=-oStrictHostKeyChecking=no
8 |
9 | if [[ "$(uname)" == "Darwin" ]]; then
10 | DOCKER_CMD=docker
11 | else
12 | DOCKER_CMD="sudo docker"
13 | fi
14 |
15 | if [[ -z $($DOCKER_CMD images | grep test-container) ]] ; then
16 | echo "Building test container"
17 | docker build -t test-container $SCRIPT_DIR > /dev/null
18 | fi
19 |
20 | echo "Testing $1"
21 | CODE_DIR=$(cd $SCRIPT_DIR/..; pwd)
22 | echo "$@"
23 | $DOCKER_CMD run \
24 | --rm \
25 | --name test \
26 | -v /var/run/docker.sock:/var/run/docker.sock \
27 | -v $CODE_DIR:$CODE_DIR -w $CODE_DIR \
28 | -e COVERALLS_TOKEN=$COVERALLS_TOKEN \
29 | -e TRAVIS_JOB_ID=$TRAVIS_JOB_ID \
30 | -e TRAVIS_BRANCH=$TRAVIS_BRANCH \
31 | -e TRAVIS_PULL_REQUEST=$TRAVIS_PULL_REQUEST \
32 | -e TRAVIS=$TRAVIS \
33 | -e TAG=$TAG \
34 | -e COMMIT=$COMMIT \
35 | test-container \
36 | sh -c "export PYTHONPATH=\$PYTHONPATH:\$PWD/test ; python test/$@"
37 |
--------------------------------------------------------------------------------
/test/unit.py:
--------------------------------------------------------------------------------
1 | import os
2 | import unittest
3 | from os.path import expanduser
4 |
5 | from util.Docker import Docker
6 |
7 |
8 | class JavaServices(unittest.TestCase):
9 | def test_maven(self):
10 | script_dir = os.path.dirname(os.path.realpath(__file__))
11 | code_dir = script_dir + "/.."
12 | home = expanduser("~")
13 | command = ['docker', 'run', '--rm', '-v', home + '/.m2:/root/.m2', '-v', code_dir + ':/usr/src/mymaven', '-w',
14 | '/usr/src/mymaven', 'maven:3.6-jdk-11', 'mvn', '-q', 'test']
15 | print(Docker().execute(command))
16 |
17 |
18 | if __name__ == '__main__':
19 | unittest.main()
20 |
--------------------------------------------------------------------------------
/test/util/Api.py:
--------------------------------------------------------------------------------
1 | import requests
2 |
3 | class Api:
4 | def noResponse(self, url):
5 | try:
6 | r = requests.get(url, timeout=5)
7 | except requests.exceptions.ConnectionError:
8 | return True
9 | return False
10 |
--------------------------------------------------------------------------------
/test/util/Docker.py:
--------------------------------------------------------------------------------
1 | import re
2 | from subprocess import Popen, PIPE
3 | from random import random
4 |
5 | # From http://blog.bordage.pro/avoid-docker-py/
6 | class Docker:
7 | def kill_and_remove(self, ctr_name):
8 | command = ['docker', 'rm', '-f', ctr_name]
9 | try:
10 | self.execute(command)
11 | return True
12 | except RuntimeError as e:
13 | print(e)
14 | return False
15 |
16 | def random_container_name(self, prefix):
17 | retstr = prefix + '-'
18 | for i in range(5):
19 | retstr += chr(int(round(random() * (122-97) + 97)))
20 | return retstr
21 |
22 | def get_container_ip(self, ctr_name):
23 | command = ['docker', 'inspect',
24 | '--format', '\'{{.NetworkSettings.IPAddress}}\'',
25 | ctr_name]
26 | return re.sub(r'[^0-9.]*', '', self.execute(command))
27 |
28 | def execute(self, command, dump_streams=False):
29 | print("Running: " + ' '.join(command))
30 | p = Popen(command, stdout=PIPE, stderr=PIPE)
31 | out, err = p.communicate()
32 | if dump_streams == True:
33 | print(out.decode('utf-8'))
34 | print(err.decode('utf-8'))
35 | return str(out.decode('utf-8'))
36 |
37 | def start_container(self, container_name="", image="", cmd="", host=""):
38 | command = ['docker', 'run', '-d', '-h', host, '--name', container_name, image]
39 | self.execute(command)
40 |
--------------------------------------------------------------------------------
/test/util/Dredd.py:
--------------------------------------------------------------------------------
1 | from util.Docker import Docker
2 | from util.Api import Api
3 | import os
4 | import unittest
5 |
6 | class Dredd:
7 | image = 'weaveworksdemos/openapi:snapshot'
8 | container_name = ''
9 | def test_against_endpoint(self, service, api_endpoint, links=[], env=[], dump_streams=False):
10 | self.container_name = Docker().random_container_name('openapi')
11 | command = ['docker', 'run',
12 | '-h', 'openapi',
13 | '--name', self.container_name,
14 | '-v', "{0}:{1}".format(os.getcwd() + "/api-spec/", "/tmp/specs/")]
15 |
16 | if links != []:
17 | [command.extend(["--link", x]) for x in links]
18 |
19 | if env != []:
20 | [command.extend(["--env", "{}={}".format(x[0], x[1])]) for x in env]
21 |
22 | command.extend([Dredd.image,
23 | "/tmp/specs/{0}.json".format(service),
24 | api_endpoint,
25 | "-f",
26 | "/tmp/specs/hooks.js".format(service)])
27 | out = Docker().execute(command, dump_streams=dump_streams)
28 |
29 | Docker().kill_and_remove(self.container_name)
30 | return out
31 |
--------------------------------------------------------------------------------
/test/util/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microservices-demo/carts/f4e80051668c6a83359c41b03a27baefa590adc8/test/util/__init__.py
--------------------------------------------------------------------------------