Welcome to the Picnic Java programming assignment! In this document you will find the specification for the assignment, and instructions how to get started and hand your solution in. Please read the document carefully.
Your task is to read and process customer orders from an InputStream
, and process them before
writing them to an OutputStream
. Reading orders is limited by time, or by the number of read
orders. Processing of orders consists of filtering, grouping and transformation.
The input stream sends each order as a single line of JSON adhering to the format described below, but does not guarantee orders to be sorted or arrive in predictable intervals.
As part of your task, you should extend the provided project in this repository. It is a Maven project and the code is structured as follows:
- Package
tech.picnic.assignment.api
contains classes that must NOT be modified. Of special interest in this package are the following two interfaces:OrderProcessorFactory
: you will be asked to create an implementation of this interface. We've created a skeleton for you already and made it service-loadable with the@AutoService
annotation in order to expedite our review process. You must not touch this annotation and also don't need to be aware of what it does.OrderStreamProcessor
: most of the assignment revolves around creating an implementation of this interface which meets various criteria. The aforementioned factory should be able to create one or more of these processor instances.
- Package
tech.picnic.assignment.impl
contains a skeleton implementation of the aforementionedOrderProcessorFactory
interface, whereas packagetech.picnic.assignment.impl.model
contains an incompleteOrder
represented as a Record (of course you are also free to replace it with a different approach). Your task is to change the code in these packages to meet the criteria described below. - The
ObjectMapperFactory
provides a bare-bone JacksonObjectMapper
that can be used for JSON (de-)serialization, though you are free to choose another approach. - The
pom.xml
file defines how Maven can build the project. Some aspects of this file must not be changed; those are indicated using comments. Make sure that your project builds usingmvn clean verify
. - Under
src/test
a disabled test is defined. Consider getting it to pass. At Picnic, we like unit-tested code. Thus, you are free to add more!
- An
OrderProcessorFactory
implementation which producesOrderStreamProcessor
instances capable of reading the order input stream described above. - The factory should produce processors that respect the given
maxOrders
andmaxTime
parameters. For example,myOrderProcessorFactory.createProcessor(100, Duration.ofSeconds(30))
should produce anOrderStreamProcessor
which for every invocation ofOrderStreamProcessor#process()
should read orders either for up to 30 seconds or until the order limit of 100 is reached, whichever condition is met first. The orders should then be processed and returned. - A processor should process the orders it receives as follows:
- Only orders with statuses
delivered
andcancelled
are retained. Orders with other statuses still count towards themaxOrders
limit but are otherwise ignored. - Orders must be grouped by delivery ID (see the grouped output format below).
- A delivery's status can be either
delivered
orcancelled
. It is delivered, iff at least one of its orders has statusdelivered
. - A delivery's total amount is the sum of all orders with status
delivered
, ornull
if none exists. - Deliveries must be sorted chronologically (ascending) by their
delivery_time
timestamp, breaking ties by ID. - Orders must be sorted by their ID (descending).
- Only orders with statuses
- A processor must write the result of the aforementioned filter, group and sort operations to the
provided
OutputStream
according to the format described below.
The input stream is specified as follows:
- No assumptions should be made about the source of this stream of messages. Perhaps they are read from a file, perhaps they arrive over a TCP stream from the other side of the world, or perhaps they are auto-generated by a test framework. ;)
- No assumptions should be made about the speed at which messages arrive. Multiple messages may arrive in brief succession, but it could also be that no messages arrive for extended periods of time.
- If no order messages are sent for a while, 'keep-alive' messages consisting of a single
\n
may be sent. - Each order adheres to the JSON type specification described below.
- Each order comprises a single line of JSON, terminated by a newline (
\n
). - If the timeout expires while reading an incoming order, this order should be discarded. All previous orders should be processed and returned as specified. The timeout should never be exceeded when reading from the stream.
Order JSON type specification
Field | JSON Type | Description |
---|---|---|
order_id |
String | A unique identifier. |
order_status |
String | Either created , delivered or cancelled . |
delivery |
Object | Delivery object, see below. |
amount |
Number | The paid amount in cents; can be null . |
Delivery JSON type specification
Field | JSON Type | Description |
---|---|---|
delivery_id |
String | A unique identifier. |
delivery_time |
String | The time this delivery is scheduled for; formatted as an ISO 8601 UTC date-time string. |
The following JSON object is an example of the kind of order that may be received from
the InputStream
. Note how it matches the specification above. There is one difference: you may
assume that the orders read from the InputStream
are on a single line, i.e. they are not formatted
using line breaks.
{
"order_id": "1234567890",
"order_status": "delivered",
"delivery": {
"delivery_id": "d923jd29j91d1gh6",
"delivery_time": "2022-05-20T11:50:48Z"
},
"amount": 6477
}
A processor must write the result to the provided OutputStream
according the following JSON
format:
[
{
"delivery_id": "d923jd29j91d1gh6",
"delivery_time": "2022-05-20T11:50:48Z",
"delivery_status": "delivered",
"orders": [
{
"order_id": "1234567890",
"amount": 6477
},
...
more
orders
here
...
],
"total_amount": 6477
},
...
more
deliveries
here
...
]
For a complete example of expected input and corresponding output, compare the contents of the following two provided files:
- Feel free to add or modify dependencies in
pom.xml
. - Do not modify the existing plugin configurations in
pom.xml
(though you can add more if needed). - Do not modify the
tech.picnic.assignment.api
package. - Do not modify
tech.picnic.assignment.impl.RecordNamingStrategyPatchModule
.
- We value clean, readable, modern, and idiomatic Java. Your code will be reviewed by other developers, so make sure it is easy to follow and well-structured.
- Avoid doing manual JSON parsing. It's prone to errors and hard to read.
- Don't feel the need to over-engineer your solution. We don't expect you to build an entire system that can scale to billions of orders. Your solution should be tailored to the problem statement. We prefer concise and simple solutions over lengthy ones. However, it should be straightforward to let the program behave differently, such as have a different timeout, filter on a different order status, etc.
Make a local clone of this repository on your machine, and do your work on a branch other
than master
. Do not make any changes to the master
branch.
Typically, candidates spend around four hours and write roughly 500 lines of non-test code to complete the assignment. It is however possible to write a "perfect" solution using much less code than that. Of course, we won't measure or in any way take into account the time you spent.
- Push your solution to
origin/<your-branch-name>
, and create a pull request to merge your changes back into themaster
branch. Please do not merge your pull request. Only you and developers at Picnic can see this pull request. We highly recommend you to fill in the template that will show up when opening the pull request to help us review your submission, however, it is not required to achieve a satisfactory result. - Create and add the label
done
to your pull request. This will notify us that your code is ready to be reviewed. Please do NOT publish your solution on a publicly available location (such as a public GitHub repository, your personal website, etc.). Also, please refrain from tagging or assigning specific Picnic employees as reviewers of your PR; we have an internal round-robin system to determine who will review your code. - Once your pull request is created and labelled, please send the link to the recruiter, and we will perform an internal review.
- In the next step, if we deem the assignment satisfactory, you are invited for a quick call to look over your solution together with the reviewers. Make sure to save a local copy (you will lose access to the repository after creating the pull request), you will be asked to keep it in front of you or to share your screen.
This process closely mimics our actual development and review cycle. We hope you enjoy it and wish you much success!