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

Added Single member route and eslint #15

Merged
merged 18 commits into from
Sep 24, 2023
Merged
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
25 changes: 25 additions & 0 deletions __tests__/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,28 @@ describe("GET /members", () => {
expect(member).toHaveProperty("github_link");
});
});

describe("GET /member/PRATHAM1ST", () => {
it("should return a 200 response", async () => {
const res = await request(app).get("/member/PRATHAM1ST");
expect(res.status).toBe(200);
});

it("should return an object", async () => {
const res = await request(app).get("/member/PRATHAM1ST");
expect(typeof res.body.data).toBe("object");
});

it("should return an object with the correct properties", async () => {
const res = await request(app).get("/member/PRATHAM1ST");
const member = res.body.data;
expect(member).toHaveProperty("name");
expect(member).toHaveProperty("username");
expect(member).toHaveProperty("profile_pic_url");
expect(member).toHaveProperty("followers");
expect(member).toHaveProperty("following");
expect(member).toHaveProperty("repositories");
expect(member).toHaveProperty("bio");
expect(member).toHaveProperty("github_link");
});
});
17 changes: 13 additions & 4 deletions app.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
require('dotenv').config()
import "dotenv/config";

import express, { Express, Request, Response } from "express";
import membersRoute from "./routes/members";
import cors from "cors";
import morgan from "morgan";
import ResponseHandler from "./components/response";
import ResponseHandler from "./components/responseHandler";

import membersRoute from "./routes/members";
import memberRoute from "./routes/member";

const corsOptions = {
origin: "*",
Expand All @@ -17,9 +20,15 @@ app.use(cors());
app.use(morgan("dev"));

app.get("/", (req: Request, res: Response) => {
return ResponseHandler.success(req, res, "Hello World", "Success");
return ResponseHandler.success({
req,
res,
data: "Hello World",
message: "Success",
});
});

app.use("/member", memberRoute);
app.use("/members", membersRoute);

export default app;
17 changes: 17 additions & 0 deletions components/errorHandler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { NextFunction, Request, Response } from "express";
import ResponseHandler from "./responseHandler";

const errorHandler = (
fn: (req: Request, res: Response, next?: NextFunction) => Promise<Response>
) =>
function (req: Request, res: Response, next: NextFunction) {
Promise.resolve(fn(req, res, next)).catch((err: Error) => {
return ResponseHandler.error({
req,
res,
message: err.message,
});
});
};

export default errorHandler;
20 changes: 0 additions & 20 deletions components/response.ts

This file was deleted.

25 changes: 25 additions & 0 deletions components/responseHandler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { Request, Response } from "express";

class ResponseHandler {
static success(options: {
req: Request;
res: Response;
data: string | object | object[] | null;
message: string;
}) {
return options.res.status(200).json({
status: true,
data: options.data,
message: options.message,
});
}

static error(options: { req: Request; res: Response; message: string }) {
return options.res.status(500).json({
status: false,
message: options.message,
});
}
}

export default ResponseHandler;
123 changes: 123 additions & 0 deletions controllers/member.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
import { Request, Response } from "express";
import membersListLatest from "../data/members-list.json";
import ResponseHandler from "../components/responseHandler";
import axios, { AxiosResponse } from "axios";
import { parseHTML } from "linkedom";

interface Member {
login: string;
username?: string;
name: null | string;
tfa_enabled: boolean;
is_public: boolean;
role: string | null;
last_active: string | null;
saml_name_id: null | string;
profile_pic_url?: null | string;
followers?: null | string;
following?: null | string;
repositories?: null | string;
bio?: null | string;
github_link?: string;
}

const getUserProfile = async (login: string) => {
const response: AxiosResponse = await axios.get(
`https://github.com/${login}`
);

if (response.status !== 200) {
throw new Error("User not found");
}

return await response.data;
};

const getUserInfo = async (html: string) => {
const { document } = parseHTML(html);
const photoSrc: HTMLAnchorElement | null = document.querySelector(
".js-profile-editable-replace > .clearfix.d-flex.d-md-block.flex-items-center.mb-4.mb-md-0 > .position-relative.d-inline-block.col-2.col-md-12.mr-3.mr-md-0.flex-shrink-0 > a"
);
const followersAndFollowing: NodeListOf<HTMLSpanElement> | null =
document.querySelectorAll(
".js-profile-editable-area.d-flex.flex-column.d-md-block > div.flex-order-1.flex-md-order-none.mt-2.mt-md-0 > div > a > span"
);

const repositories: HTMLSpanElement | null = document.querySelector(
".Layout-main > div > nav > a > .Counter"
);

const bio: HTMLDivElement | null =
document.querySelector("div[data-bio-text]");

if (!followersAndFollowing)
console.log("Followers and Following not found");

if (!photoSrc) console.log("Photo not found");

if (!repositories) console.log("Repositories not found");

return {
profile_pic_url: photoSrc?.href,
followers: followersAndFollowing[0]
? followersAndFollowing[0].innerText
: "0",
following: followersAndFollowing[1]
? followersAndFollowing[1].innerText
: "0",
repositories: repositories?.title,
bio: bio?.innerText ?? "",
};
};

export const updateSingleMember = async (member: Member) => {
const html = await getUserProfile(member.login);
const { profile_pic_url, followers, following, repositories, bio } = await getUserInfo(html);
member.username = member.login;
member.github_link = `https://github.com/${member.login}`;
member.profile_pic_url = profile_pic_url;
member.followers = followers ?? "0";
member.following = following ?? "0";
member.repositories = repositories ?? "0";
member.bio = bio ?? "";

return member;
};

export async function PATCH(req: Request, res: Response) {
const getGithubUserName = req.params.username;

const member = membersListLatest.find((member) => member.login === getGithubUserName);
if (!member) throw new Error("Member not found");

const updatedMember = await updateSingleMember(member);
if (!updatedMember) throw new Error("Member not found");

console.log("updatedMember", updatedMember);

return ResponseHandler.success({
req,
res,
data: updatedMember,
message: "Member found",
});
}

export async function GET(req: Request, res: Response) {
const getGithubUserName = req.params.username;
const membersListArray: Member[] = membersListLatest;
const member = membersListArray.find(
(member) => member.login === getGithubUserName
);

if (!member) throw new Error("Member not found");

return ResponseHandler.success({
req,
res,
data: member,
message: "Member found",
});
}

export default { GET, PATCH };
124 changes: 17 additions & 107 deletions controllers/members.ts
Original file line number Diff line number Diff line change
@@ -1,119 +1,29 @@
import { Request, Response } from "express";
import axios, { AxiosResponse } from "axios";
import { parseHTML } from "linkedom";
import membersList from "../data/members-list-old.json";
import membersListLatest from "../data/members-list.json";
import ResponseHandler from "../components/response";

interface Member {
login: string;
username?: string;
name: null | string;
tfa_enabled: boolean;
is_public: boolean;
role: string | null;
last_active: string | null;
saml_name_id: null | string;
profile_pic_url?: null | string;
followers?: null | string;
following?: null | string;
repositories?: null | string;
bio?: null | string;
github_link?: string;
}

const getUserProfile = async (login: string) => {
const response: AxiosResponse = await axios.get(
`https://github.com/${login}`
);

if (response.status !== 200) {
throw new Error("User not found");
}

return await response.data;
};

const getUserInfo = async (html: string) => {
const { document } = parseHTML(html);
const photoSrc: HTMLAnchorElement | null = document.querySelector(
".js-profile-editable-replace > .clearfix.d-flex.d-md-block.flex-items-center.mb-4.mb-md-0 > .position-relative.d-inline-block.col-2.col-md-12.mr-3.mr-md-0.flex-shrink-0 > a"
);
const followersAndFollowing: NodeListOf<HTMLSpanElement> | null =
document.querySelectorAll(
".js-profile-editable-area.d-flex.flex-column.d-md-block > div.flex-order-1.flex-md-order-none.mt-2.mt-md-0 > div > a > span"
);

const repositories: HTMLSpanElement | null = document.querySelector(
".Layout-main > div > nav > a > .Counter"
);

const bio: HTMLDivElement | null =
document.querySelector("div[data-bio-text]");

if (!followersAndFollowing)
console.log("Followers and Following not found");

if (!photoSrc) console.log("Photo not found");

if (!repositories) console.log("Repositories not found");

return {
profile_pic_url: photoSrc?.href,
followers: followersAndFollowing[0]
? followersAndFollowing[0].innerText
: "0",
following: followersAndFollowing[1]
? followersAndFollowing[1].innerText
: "0",
repositories: repositories?.title,
bio: bio?.innerText ?? "",
};
};

const updateSingleMember = async (member: Member) => {
const html = await getUserProfile(member.login);
const { profile_pic_url, followers, following, repositories, bio } =
await getUserInfo(html);
member.username = member.login;
member.github_link = `https://github.com/${member.login}`;
member.profile_pic_url = profile_pic_url;
member.followers = followers ?? "0";
member.following = following ?? "0";
member.repositories = repositories ?? "0";
member.bio = bio ?? "";
};
import ResponseHandler from "../components/responseHandler";
import { updateSingleMember } from "./member";

export async function PATCH(req: Request, res: Response) {
try {
const membersListArray: Member[] = membersList;

await Promise.all(
membersListArray.map(async (member) => updateSingleMember(member))
);
const updateMembersListArray = await Promise.all(
membersList.map((member) => updateSingleMember(member))
);

return ResponseHandler.success(
req,
res,
membersListArray,
"Members list updated"
);
} catch (err: any) {
return ResponseHandler.error(req, res, err);
}
return ResponseHandler.success({
req,
res,
data: updateMembersListArray,
message: "Members list updated",
});
}

export async function GET(req: Request, res: Response) {
try {
return ResponseHandler.success(
req,
res,
membersListLatest,
"Members list"
);
} catch (err: any) {
return ResponseHandler.error(req, res, err);
}
return ResponseHandler.success({
req,
res,
data: membersListLatest,
message: "Members list",
});
}

export default { GET, PATCH };
Loading