Add a note to the readme about the need for unit test coverage
[sig-chat-ex-feb-2019.git] / README.md
blob75489f0064ae682913fd51585d701cfa41ef2d12
1 ## Chat server exercise 1.2 notes
3 The primary focus of this document is on explication of existing code and
4 suggestions for improving robustness and clarity rather than to speculate about
5 a feature-rich chat experience.
7 ### Building and running the server
9 Running `gradle build` will bundle an executable uber jar in build/libs.
11 To run the integration test: `gradle integrationTest`
13 **Usage:**
14 ```
15 $ java -jar build/libs/signal-rest-api-exercise-v1.2-all.jar -h
16 Usage: simple-chat-server [-hV] [-a=ADDRESS] [-f=DB_FILE] -p=PORT
17 Starts the chat server.
18   -a, --address=ADDRESS   server address (default: localhost)
19   -f, --dbFile=DB_FILE    path to a file where the chat db is stored (default:
20                             chats_testing.db)
21   -h, --help              Show this help message and exit.
22   -p, --port=PORT         server port
23   -V, --version           Print version information and exit.
24 ```
26 Alternatively, `gradle run` will execute the main method as well.
28 * Option --dbFile is the location of the [MapDB](http://www.mapdb.org)
29   database. If not specified it will be chats_testing.db in the working
30   directory.
31 * Port (-p, --port) is a required option.
33 #### Examples:
35 **listen only on localhost:**
36 ```
37 $ java -jar build/libs/signal-rest-api-exercise-v1.2-all.jar -a localhost -p 6000
38 ... INFO : e.n.c.s.h.d.Dispatcher loaded 2 route handlers
39 ... INFO : e.n.c.s.Server loaded 200 users from contacts
40 ... INFO : e.n.c.s.a.Listener Listening on /127.0.0.1:6000
41 ```
43 **listen everywhere and specify a db (requires privilege to bind port 80):**
44 ```
45 $ java -jar build/libs/signal-rest-api-exercise-v1.2-all.jar -a :: -p 80 --dbFile /var/run/simple-chat/chats.db
46 ... INFO : e.n.c.s.h.d.Dispatcher loaded 2 route handlers
47 ... INFO : e.n.c.s.Server loaded 200 users from contacts
48 ... INFO : e.n.c.s.a.Listener Listening on /0:0:0:0:0:0:0:0:80
49 ```
51 ### Server endpoints, protocol, request / response
53 See schemas/ for JSON schemas of chat and message.
55 #### POST /chats creates a chat between two users:
56 ```http
57 POST /chats HTTP/1.1
58 Host: localhost:3335
59 User-Agent: curl/7.64.0
60 Accept: */*
61 Content-Length: 54
62 Content-Type: application/json
65     "id": 1,
66     "participantIds": [51201, 28463]
68 ---
69 HTTP/1.1 200 OK
70 ```
72 * The number of participantIds must be equal to two, otherwise 400 Bad Request
73 * Each participant must be in the other's contact list, otherwise 403 Forbidden
74 * The request body must be a JSON chat document, otherwise 400 Bad Request
76 #### GET /chats?userId={userId} retrieves a user's chats:
77 ``` http
78 GET /chats?userId=28463 HTTP/1.1
79 Host: localhost:3335
80 User-Agent: curl/7.64.0
81 Accept: */*
82 ---
83 HTTP/1.1 200 OK
84 Content-Type: application/json; charset=utf-8
85 Content-Length: 41
86 Content-Encoding: identity
88 [{"id":1,"participantIds":[51201,28463]}]
89 ```
91 * The userId must be an integer (Java long), otherwise 400 Bad Request
93 #### POST /chats/{chatId}/messages adds a message to a chat
94 ``` http
95 POST /echo HTTP/1.1
96 Host: localhost:3335
97 User-Agent: curl/7.64.0
98 Accept: */*
99 Content-Length: 172
100 Content-Type: application/json
103     "id": "6cfd98ae-ab8a-4722-a385-dad87e5f0207",
104     "timestamp": 101,
105     "message": "Hi, how's it going?",
106     "sourceUserId": 28463,
107     "destinationUserId": 51201
110 HTTP/1.1 200 OK
113 * The chatId must be an integer (Java long), otherwise 400 Bad Request
114 * The chat must exist, otherwise, 404 Not Found
115 * The request body must be a JSON message document, otherwise 400 Bad Request
116 * The set of chat participantIds must be equivalent to a set containing the
117   source and destination userIds of the message, otherwise 403 Forbidden
119 #### GET /chats/{chatId}/messages retrieves the messages in a chat
120 ``` http
121 GET /chats/1/messages HTTP/1.1
122 Host: localhost:3335
123 User-Agent: curl/7.64.0
124 Accept: */*
126 HTTP/1.1 200 OK
127 Content-Type: application/json; charset=utf-8
128 Content-Length: 270
129 Content-Encoding: identity
131 [{"id":"0d1e05a1-0b57-44a7-967a-1d7415cbedec","timestamp":100,"message":"Hello!","sourceUserId":51201,"destinationUserId":28463},{"id":"6cfd98ae-ab8a-4722-a385-dad87e5f0207","timestamp":101,"message":"Hi, how's it going?","sourceUserId":28463,"destinationUserId":51201}]
134 * The chatId must be an integer (Java long), otherwise 400 Bad Request
135 * The chat must exist, otherwise, 404 Not Found
137 ### Program structure
139 #### Package layout
140 * ex.nio.chat.server: server loop, set up nio pipeline
141   * accept: first in nio pipeline, select on OP_ACCEPT
142   * annotations: @Path used for routing to handlers based on URI path
143   * data: model objects and a storage layer on MapDB
144   * http: pojos and constants for http request and response
145     * dispatch: second in nio pipeline, select on OP_READ, hand requests to
146       parser, route realized requests to activities
147     * parse: incremental request parsing
148   * method: endpoints and method handling
150 ### Next Steps
152 #### Code organization and modularity
154 Consider using an injection framework to achieve decoupling between server,
155 controller, instrumentation, and data components. At present, the listener and
156 dispatcher are decoupled from the application logic by non standardized use of
157 reflection to search for annotations that bind routing and priority data to an
158 interface type known to the dispatcher.
160 Consider using a lifecycle manager such as Spring to ensure that startup and
161 shutdown occurs in a graceful and orderly manner.
163 Add timing and count metrics for use with profiling and assessing performance.
165 Add a coverage tool and increase unit testing such that line coverage is 100%
166 and branch coverage is > 90%.
168 #### Architecture
170 ##### Chat protocol
172 The protocol and schemas should be versioned.
174 User chats are presently returned in an arbitrary order. This is okay in the
175 likely case that each front end instance is expected to maintain its own
176 ordering. It may be nice to allow chats to be ordered by creation time, which
177 would require an update to the schema.
179 An endpoint should be added to allow users to request to be contacts.
181 ##### Storage
183 To achieve horizontal scaling with a DB such as Postgres, data can
184 be partitioned. Chat data lends itself relatively well to partitioning.
186 With a storage system designed for horizontal scaling such as DynamoDB, it is a
187 requirement to be able to perform consistent reads (which DynamoDB does provide).
189 ##### Caching
191 Individual messages are immutable and may be cached indefinitely. The server
192 should return cache control headers and allow a 304 response for GET
193 /chat/{chatId}/messages. (Even with theoretically infinitely cacheable data, it
194 is best to set a cache timeout of at most 30 minutes.) It would be beneficial
195 to paginate message responses and allow incremental message retrieval.
197 It is unclear whether chats are indefinitely cacheable as it is possible to
198 relax the restriction on participantIds to allow more than two participants.
200 ##### Security
202 Users should be authenticated and only authorized to access chats in which they
203 are participants and messages of those chats.
205 ##### Server scaling, performance, and robustness
207 The server should better support HTTP/1.1 including common Content-Encodings
208 such as gzip, Transfer-encoding chunked.
210 Support HTTP 2.
212 Profile against expected and abnormal request loads and tune accordingly.
214 Since routes are static at runtime, an in memory cache of routes matching paths
215 could be useful depending on profiling about time used matching paths.