You are on page 1of 5

Everseen Node.

JS practical test

ALL code must be PLAIN node.js using ONLY STANDARD node.js libraries. This is not
because we have anything against using npm modules, but rather to properly asses the
candidate programming skills and graps of core concepts isolated from the “magic” of
community provided modules.

NOTE 1: The wording in some cases is intentionally vague to give room to personal
interpretation
NOTE 2: Please record and submit the timing per each solve and per each bonus round

Basics
1. Async/await
Refactor the code below with async/await so there are no more callbacks:
function fetchData(cb) {
setTimeout(() => {
if (Math.random() < 0.2) cb(new Error('fetch error'))
else cb(null, { fake: 'data' })
}, 1000)
}

function processData(data, cb) {


setTimeout(() => {
if (Math.random() < 0.2) cb(new Error('process error'))
else cb(null, { fake: 'processed' + data.fake })
}, 1000)
}

function process(cb) {
fetchData((err, data) => {
if (err) cb(err)
else processData(data, (err, processedData) => {
if (err) cb(err)
else cb(null, processedData)
})
})
}

process((err, data) => {


if (err) {
console.error('Error processing data', err)
} else {
fs.writeFile('out.json', JSON.stringify(data), err => {
if (err) console.error('Error writing data', err)
else console.log('Done')
})
}
})

Bonus round: can you make it so that it runs as an executable and without any
.then(...)?

2. This
Without running the code can you tell what is the console output and why?
p = 10
const x = {
p: 5,
f: function () { return this.p }
}
const { f } = x
const y = {
p: 20,
f
}
console.log(f())
console.log(x.f())
console.log(y.f())

What if we remove p = 10? why?

3. Function generators
Without running it can you tell what is the output of the following snippet?:
function* counter(count) {
while(count--) yield count
}
for(const v of counter(5)) {
console.log(v)
}

Modify it so it runs in the other direction and resets for X times when it reaches the end
(like an odometer)

4. Parsing strings
Add code to this:
const log = `
2020-01-02 01:02:03.100+05:00 DEBUG This is a dummy DEBUG message
2020-01-02 01:02:03.200+05:00 INFO This is a random INFO message
2020-01-02 01:02:03.300+05:00 ERROR This is an test ERROR message
2020-01-02 01:02:03.400+05:00 INFO This is a another random INFO message
2020-01-02 01:02:03.500+05:00 ERROR This is an different test ERROR message
2020-01-02 01:02:03.600+05:00 DEBUG This is a dummy test DEBUG message
`

So when executed the console output looks like this:


{
"date": "2020-01-02",
"time": "01:02:03.100",
"tz": "+05:00",
"level": "DEBUG",
"message": "This is a DEBUG message"
}
{
"date": "2020-01-02",
"time": "01:02:03.300",
"tz": "+05:00",
"level": "ERROR",
"message": "This is an ERROR message"
}
{
"date": "2020-01-02",
"time": "01:02:03.500",
"tz": "+05:00",
"level": "INFO",
"message": "This is an INFO message"
}

Bonus round: can you make it using regular expressions?

5. ?
Add your code to the snippet bellow so that:
console.log("1,2,3,4".add()) // prints 10
console.log("1,2,3,4".mul()) // prints 24

Bonus round: Can you make it in only 2 (not too long) lines?

Advanced
These build upon eachother to an increasing level of complexity/difficulty.

6. Large files
Use the code from #4 in prev section to parse large files without reading them all into
memory (should work with >1GB files)
function parse(logFile) {
// your code here
}
parse('logfile.log')

7. Filtering
Transform the code from #6 to output only messges of a provided set of levels and contain
certain strings:
parse('logFile', ['ERROR','INFO'], ['test'])
// outputs only errors or infos that contain `test`

Bonus round1: can you make it case insensitive and match regexes instead of strings
Bonus round2: can you refactor it so this code would output the same:
for await (const s of iteratorParse('logfile.log', ['error', 'info'],
['test'])) {
console.log(s)
}

8. CLI
Based on #7 create a logparser.js that takes the following form of command line:
node logparser.js -f somefile.log -f someotherfile.log -l info -l error -s
testing123

and outputs the error and info logs from all the files specified with -f and have
testing123 somewhere in the message

Bonus round: can you make it that it works as CLI and also as a module

9. REST
Create a http REST service so that the cli options can be passed to it via POST args
curl -X POST http://localhost:5000/logparser -H 'Content-type:
application/json' -d '{"f": ["somefile.log","someotherfile.log"], "l":
["error","info"], "s": "testing123"}'
# outputs same as the cli

10. Performance
Add a throttling mechanism to the REST API that allows a configurable max rate of requests
per second for the /logparser route.
Bonus round: Create a small smart REST client that calls the API and in case the request is
rejected due to throtlling it polls at regular intervals until it either succeeds or fails for a
configurable number of tries.
11. Scaling
Transform the REST service so it works as a cluster, ie. load ballanced with a configurable
number of workers serving the /logparser route
Bonus round: Add a health route that shows stats about the cluster and its nodes

12. Security
Protect the API with a simple bearer token authentication mechanism by adding an /auth
route through which a client can exchange user/pass credentials with a signed/encrypted
token that grants access to the main /logparser endpoint

13. Delivery
Create a dockerfile that bundles the API and the CLI and when run without args starts the
server and when ran with args runs the cli with the passed args.

You might also like