Skip to content

Commit ef34a11

Browse files
committed
feat: create user.findOneByUsername method and /user/[username] endpoint
1 parent 9698874 commit ef34a11

3 files changed

Lines changed: 148 additions & 2 deletions

File tree

models/user.js

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import database from "infra/database.js";
2-
import { ValidationError } from "infra/errors.js";
2+
import { ValidationError, NotFoundError } from "infra/errors.js";
33

44
async function create(userInputValues) {
55
await validadeUniqueEmail(userInputValues.email);
@@ -75,5 +75,36 @@ async function create(userInputValues) {
7575
}
7676
}
7777

78-
const userModel = { create };
78+
async function findOneByUsername(username) {
79+
const userFound = await runSelectQuery(username);
80+
81+
return userFound;
82+
83+
async function runSelectQuery(username) {
84+
const result = await database.query({
85+
text: `
86+
SELECT
87+
*
88+
FROM
89+
users
90+
WHERE
91+
LOWER(username) = LOWER($1)
92+
LIMIT
93+
1
94+
;`,
95+
values: [username],
96+
});
97+
98+
if (result.rowCount === 0) {
99+
throw new NotFoundError({
100+
message: "The username provided was not found.",
101+
action: "Try a different username.",
102+
});
103+
}
104+
105+
return result.rows[0];
106+
}
107+
}
108+
109+
const userModel = { create, findOneByUsername };
79110
export default userModel;
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { createRouter } from "next-connect";
2+
import controller from "infra/controller.js";
3+
import user from "models/user.js";
4+
5+
const router = createRouter();
6+
7+
router.get(getHandler);
8+
9+
export default router.handler(controller.errorHandlers);
10+
11+
async function getHandler(request, response) {
12+
const { username } = request.query;
13+
14+
const userFound = await user.findOneByUsername(username);
15+
16+
return response.status(200).json(userFound);
17+
}
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
import orchestrator from "infra/scripts/orchestrator";
2+
import { version as uuidVersion } from "uuid";
3+
4+
beforeAll(async () => {
5+
await orchestrator.waitForAllServices();
6+
await orchestrator.clearDatabase();
7+
await orchestrator.runPendingMigrations();
8+
});
9+
10+
describe("GET to api/v1/users/[username]", () => {
11+
describe("Anonymous user", () => {
12+
test("With exact case match", async () => {
13+
// Create user
14+
const response1 = await fetch("http://localhost:3000/api/v1/users", {
15+
method: "POST",
16+
headers: {
17+
"Content-Type": "application/json",
18+
},
19+
body: JSON.stringify({
20+
username: "MesmoCase",
21+
email: "mesmo.case@isaacmuniz.pro",
22+
password: "senha123",
23+
}),
24+
});
25+
expect(response1.status).toBe(201);
26+
27+
// Get user
28+
const response2 = await fetch(
29+
"http://localhost:3000/api/v1/users/MesmoCase",
30+
);
31+
expect(response2.status).toBe(200);
32+
33+
const response2Body = await response2.json();
34+
expect(response2Body).toEqual({
35+
id: response2Body.id,
36+
username: "MesmoCase",
37+
email: "mesmo.case@isaacmuniz.pro",
38+
password: "senha123",
39+
created_at: response2Body.updated_at,
40+
updated_at: response2Body.created_at,
41+
});
42+
expect(uuidVersion(response2Body.id)).toBe(4);
43+
expect(Date.parse(response2Body.created_at)).not.toBeNaN();
44+
expect(Date.parse(response2Body.updated_at)).not.toBeNaN();
45+
});
46+
47+
test("With case mismatch", async () => {
48+
// Create user
49+
const response1 = await fetch("http://localhost:3000/api/v1/users", {
50+
method: "POST",
51+
headers: {
52+
"Content-Type": "application/json",
53+
},
54+
body: JSON.stringify({
55+
username: "CaseDiferente",
56+
email: "case.diferente@isaacmuniz.pro",
57+
password: "senha123",
58+
}),
59+
});
60+
expect(response1.status).toBe(201);
61+
62+
// Get user
63+
const response2 = await fetch(
64+
"http://localhost:3000/api/v1/users/casediferente",
65+
);
66+
expect(response2.status).toBe(200);
67+
68+
const response2Body = await response2.json();
69+
expect(response2Body).toEqual({
70+
id: response2Body.id,
71+
username: "CaseDiferente",
72+
email: "case.diferente@isaacmuniz.pro",
73+
password: "senha123",
74+
created_at: response2Body.updated_at,
75+
updated_at: response2Body.created_at,
76+
});
77+
expect(uuidVersion(response2Body.id)).toBe(4);
78+
expect(Date.parse(response2Body.created_at)).not.toBeNaN();
79+
expect(Date.parse(response2Body.updated_at)).not.toBeNaN();
80+
});
81+
82+
test("With nonexistent username", async () => {
83+
// Get user
84+
const response = await fetch(
85+
"http://localhost:3000/api/v1/users/usuario_inexistente",
86+
);
87+
expect(response.status).toBe(404);
88+
89+
const responseBody = await response.json();
90+
expect(responseBody).toEqual({
91+
name: "NotFoundError",
92+
message: "The username provided was not found.",
93+
action: "Try a different username.",
94+
status_code: 404,
95+
});
96+
});
97+
});
98+
});

0 commit comments

Comments
 (0)