Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ticket-creation-server #4

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 12 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,18 @@ d. **Bonus** Step *a* wasn't enough - some tickets have long content. Add a show
a. Agents are complaining that our search functionality isn't working properly. They gave the example that when searching for "wix store", the ticket titled "Search bar for my wix store" (id `6860d043-f551-58c8-84d6-f9e6a8cb0cb2`) is not returned. Checking the data, that ticket does exist.. Find the issue and fix it.
Friendly reminder to commit and push after completing this step.

b. We're showing only 20 tickets but agents can swear there are more. Solve this problem.
**Keep in mind the number of tickets is planned to grow exponentially very soon so make sure to think of a proper solution.**
Friendly reminder to commit and push after completing this step.
b. *Backend only feature* - add an API that will allow creating new tickets. The endpoint should be `/tickets` and the HTTP method should be `POST`.
The data passed to this API should contain "title", "content" and "userEmail", for example:
```
{
"title": "My new ticket!",
"content": "This is the new ticket's content",
"userEmail": "[email protected]",
}
```
When creating the ticket, note the missing parameters that are crucial for the app to work and fill them in a way that will be consistent with the rest of the app.
The ticket created shoud be inserted to the top of the list. In other words, after `POST`ing the above payload to `/tickets`, the first ticket in the list should be one with the "My new ticket!"



#### 2C - Bonus Task
Expand Down
1,408 changes: 1 addition & 1,407 deletions server/data.json

Large diffs are not rendered by default.

25 changes: 25 additions & 0 deletions server/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import express from 'express';

import bodyParser = require('body-parser');
import { tempData } from './temp-data';
import { writeFileSync } from 'fs';

const app = express();

Expand All @@ -27,6 +28,30 @@ app.get('/api/tickets', (req, res) => {
res.send(paginatedData);
});

function uuidv4() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
}

app.post('/api/tickets', (req, res) => {
const {title, content, userEmail} = req.body;

const ticket = {
title,
content,
userEmail,
id: uuidv4(),
creationTime: Date.now()
}
tempData.unshift(ticket);

writeFileSync('./data.json', JSON.stringify(tempData), 'utf-8');

res.status(201).send(ticket);
});

app.listen(PORT);
console.log('server running', PORT)

3 changes: 1 addition & 2 deletions server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@
"version": "1.0.0",
"main": "index.js",
"scripts": {
"start": "ts-node-dev index.ts",
"d": "ts-node index.ts"
"start": "ts-node-dev --ignore-watch=data.json index.ts"
},
"author": "",
"license": "ISC",
Expand Down
3 changes: 0 additions & 3 deletions server/temp-data.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
import {Ticket} from '@ans-exam/client/src/api';

import * as fs from 'fs';
import Chance from 'chance';

const data = require('./data.json');

export const tempData = data as Ticket[];
Expand Down
6 changes: 5 additions & 1 deletion tester/e2e.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
const puppeteer = require('puppeteer');
const serverData = require('../server/data.json');


import { readFileSync } from 'fs';

let browser;
let page;
Expand Down Expand Up @@ -34,6 +36,7 @@ describe("Titles", () => {
});

test('first title content is correct', async () => {
const serverData = JSON.parse(readFileSync('../server/data.json', 'utf8'));
await goToMainPage();
const titles = await page.$$('.title')

Expand All @@ -42,6 +45,7 @@ describe("Titles", () => {
});

test('last title content is correct', async () => {
const serverData = JSON.parse(readFileSync('../server/data.json', 'utf8'));
await goToMainPage();
const titles = await page.$$('.title')

Expand Down
2 changes: 1 addition & 1 deletion tester/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"description": "",
"main": "sum.js",
"scripts": {
"test": "jest"
"test": "jest --runInBand"
},
"dependencies": {
"jest": "^26.6.3",
Expand Down
110 changes: 110 additions & 0 deletions tester/ticket-creation.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import { readFileSync } from 'fs';

import axios from 'axios';

describe("POST /tickets", () => {

test('exists and does not return 404', async () => {

const status = await axios.post('http://localhost:3232/api/tickets', {title: 'bob 222', content: 'sdgfdggdf', userEmail: '[email protected]'})
.then((res) => res.status, err => err.response.status);

expect(status).not.toBe(404);
});

test('adds the send payload as a ticket', async () => {
const newTicket = {title: 'bob 111', content: 'some content', userEmail: '[email protected]'};
await axios.post('http://localhost:3232/api/tickets', newTicket)

const tickets = await axios.get('http://localhost:3232/api/tickets').then((res) => res.data);

const newTicketFromServer = tickets.find(t => t.title === newTicket.title);


expect(newTicketFromServer.title).toBe(newTicket.title);
expect(newTicketFromServer.content).toBe(newTicket.content);
expect(newTicketFromServer.userEmail).toBe(newTicket.userEmail);
});

test('created ticket has an id', async () => {
const newTicket = {title: 'bob 222', content: 'some content', userEmail: '[email protected]'};
await axios.post('http://localhost:3232/api/tickets', newTicket)

const tickets = await axios.get('http://localhost:3232/api/tickets').then((res) => res.data);

const newTicketFromServer = tickets.find(t => t.title === newTicket.title);

expect(newTicketFromServer.id).toBeDefined();
})

test('created ticket id is a valid guid', async () => {
const newTicket = {title: 'bob 3333', content: 'some content', userEmail: '[email protected]'};
await axios.post('http://localhost:3232/api/tickets', newTicket)

const tickets = await axios.get('http://localhost:3232/api/tickets').then((res) => res.data);

const newTicketFromServer = tickets.find(t => t.title === newTicket.title);

const guidPattern = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;


expect(newTicketFromServer.id).toMatch(guidPattern)
})

test('created ticket has a creation time', async () => {
const newTicket = {title: 'bob 4444', content: 'some content', userEmail: '[email protected]'};
await axios.post('http://localhost:3232/api/tickets', newTicket)

const tickets = await axios.get('http://localhost:3232/api/tickets').then((res) => res.data);
const newTicketFromServer = tickets.find(t => t.title === newTicket.title);

expect(typeof newTicketFromServer.creationTime).toBe('number');
})

test('bonus - new ticket is returned as the response', async () => {
const newTicket = {title: 'bob 5555', content: 'some content', userEmail: '[email protected]'};
const response = await axios.post('http://localhost:3232/api/tickets', newTicket)
.then((res) => res.data);


expect(response.title).toBe(newTicket.title);
expect(response.content).toBe(newTicket.content);
expect(response.userEmail).toBe(newTicket.userEmail);
});

test('bonus - no extra params are returned', async () => {
const newTicket = {title: 'bob 42', content: 'some content', userEmail: '[email protected]', extra: 222};
await axios.post('http://localhost:3232/api/tickets', newTicket)
.then((res) => res.data);

const tickets = await axios.get('http://localhost:3232/api/tickets').then((res) => res.data);
const newTicketFromServer = tickets.find(t => t.title === newTicket.title);


expect(newTicketFromServer.extra).not.toBeDefined();
});

test('bonus 2 - API returns 201', async () => {
const newTicket = {title: 'bob 5555', content: 'some content', userEmail: '[email protected]'};
const status = await axios.post('http://localhost:3232/api/tickets', newTicket)
.then((res) => res.status);

expect(status).toBe(201);
});

test('bonus 3 - data is saved to the json file and not just in memory', async () => {
const newTicket = {title: 'bob 6666', content: 'some content', userEmail: '[email protected]'};

await axios.post('http://localhost:3232/api/tickets', newTicket);


const file = readFileSync('../server/data.json', 'utf-8');
const ticketsInFile = JSON.parse(file);

const ticketInFile = ticketsInFile.find((t) => t.title === newTicket.title);

expect(ticketInFile).toBeDefined();
expect(ticketInFile.title).toBe(newTicket.title);
});

})