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

Event Service #475

Merged
merged 34 commits into from
Nov 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
0a1072f
Initialize db
VictiniX888 May 25, 2023
f2d54fa
Update .gitignore to ignore local env
VictiniX888 May 26, 2023
b7a0eb9
Add dockerfile for db setup
VictiniX888 May 27, 2023
fb2b315
Add nx commands for db setup
VictiniX888 May 27, 2023
511dc93
Change docker container name, add trigram index for event name
VictiniX888 May 31, 2023
cca3ef5
Implement api endpoints
VictiniX888 Jun 9, 2023
7730de0
Attempt to implement calendar widget
VictiniX888 Jun 28, 2023
89020a7
Add calendar event display and correct margins
VictiniX888 Jul 5, 2023
a4fc581
Implement events page styling
VictiniX888 Jul 6, 2023
e95c7ac
Interface with backend, implement admin functionality
VictiniX888 Jul 24, 2023
49c481d
Implement event tagging
VictiniX888 Jul 30, 2023
5a48238
Add prod env vars
VictiniX888 Aug 3, 2023
db0f852
Disable admin enpoints temporarily
VictiniX888 Aug 3, 2023
30d2c3a
Refector to Chalice
VictiniX888 Aug 19, 2023
846b501
Fix project structure to allow Chalice to deploy
VictiniX888 Aug 20, 2023
d0a4476
Add Nx command for serving locally through Chalice
VictiniX888 Aug 31, 2023
cdf3d3f
Fix error when installing dependencies for event service
VictiniX888 Sep 19, 2023
deaf5db
Refactor to use UUID instead of serial
VictiniX888 Sep 19, 2023
e846be9
Fix missing semicolons in sql files
VictiniX888 Sep 19, 2023
5c11c97
Update nx deploy command to work with chalice
VictiniX888 Sep 19, 2023
a6c372b
Outline event service mobile list
VictiniX888 Sep 21, 2023
26f71e8
Add styling for event service mobile list
VictiniX888 Oct 3, 2023
90e7b98
Use cockroachdb
VictiniX888 Oct 4, 2023
c39a0de
Connect event calendar to API
VictiniX888 Oct 7, 2023
488d582
Show events in mobile in calendar
VictiniX888 Oct 8, 2023
8488fd2
Fix mobile view transition bugs, show pinned events with toggle
VictiniX888 Oct 10, 2023
46b8000
Uncomment admin endpoints, add RBAC
VictiniX888 Oct 31, 2023
8949e9b
Implement API auth on frontend
VictiniX888 Oct 31, 2023
67bc4d8
Add delete event endpoint, fix add event endpoint when undefined params
VictiniX888 Oct 31, 2023
31fa405
Fix leaderboard and other battlepas API (move to nextjs api routes)
VictiniX888 Oct 24, 2023
a247215
Redesign, cherry pick from Adam's branch
VictiniX888 Nov 3, 2023
3d2fc8b
Update .env.example to include events api
VictiniX888 Nov 3, 2023
784ebc6
Merge remote-tracking branch 'origin/main' into event-service
VictiniX888 Nov 3, 2023
18ea260
Update package.json
VictiniX888 Nov 3, 2023
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
4 changes: 3 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,11 @@ NEXT_PUBLIC_DISABLE_SSO=true to disable, anything else for enable
HIBISCUS_FEATURE_FLAG_REDIS_URL=redis://<username>:<password>@<host>:<port>
HIBISCUS_FEATURE_FLAG_MONGO_URI=PLACEHOLDER

NEXT_PUBLIC_HIBISCUS_EVENTS_API_URL=PLACEHOLDER

NEXT_PUBLIC_TALLY_APPS_2023_X=https://tally.so/embed/<formID>

NEXT_PUBLIC_DISCORD_API_URL=PLACEHOLDER

NEXT_PUBLIC_WAIVER_URL=PLACEHOLDER
NEXT_PUBLIC_HACKER_PACKET_URL=PLACEHOLDER
NEXT_PUBLIC_HACKER_PACKET_URL=PLACEHOLDER
82 changes: 31 additions & 51 deletions apps/dashboard/common/apis/battlepass/battlepass.api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ import { faker } from '@faker-js/faker';
import { HibiscusSupabaseClient } from '@hibiscus/hibiscus-supabase-client';
import { SupabaseClient } from '@supabase/supabase-js';
import { BonusPointsStatus } from './types';
import { container } from 'tsyringe';
import { BattlePassRepository } from 'apps/dashboard/repository/battlepass.repository';
import axios from 'axios';

const getNumberStatusBonusPoint = (status: BonusPointsStatus) => {
switch (status) {
Expand Down Expand Up @@ -34,11 +37,11 @@ export interface BattlepassAPIInterface {
page_number: number;
page_count: number;
leaderboard: {
user_id: string;
first_name: string;
last_name: string;
bonus_points: number;
event_points: number;
total_points: number;
}[];
};
}>;
Expand Down Expand Up @@ -77,11 +80,11 @@ export class BattlepassAPI implements BattlepassAPIInterface {
page_number: number;
page_count: number;
leaderboard: {
user_id: string;
first_name: string;
last_name: string;
bonus_points: number;
event_points: number;
total_points: number;
}[];
};
}> {
Expand All @@ -92,11 +95,11 @@ export class BattlepassAPI implements BattlepassAPIInterface {
page_count: pageSize,
leaderboard: Array.from(Array(pageSize).keys())
.map(() => ({
user_id: faker.datatype.uuid(),
first_name: faker.name.firstName(),
last_name: faker.name.lastName(),
bonus_points: faker.datatype.number(),
event_points: faker.datatype.number(),
total_points: faker.datatype.number(),
}))
.sort(
(a, b) =>
Expand All @@ -107,46 +110,30 @@ export class BattlepassAPI implements BattlepassAPIInterface {
},
};
}
const res = await this.client.from('leaderboard').select(
`user_profiles(user_id,first_name,last_name),
bonus_points, event_points`
);
if (!res.data) res.data = [];
const leaderboard = res.data
.sort((a, b) => {
return (
b.bonus_points + b.event_points - (a.bonus_points + a.event_points)
);
})
.slice(pageNum * pageSize - pageSize, pageNum * pageSize);
const returnData = {
data: {
page_number: pageNum,
page_count: pageSize,
leaderboard: leaderboard.map((item) => {
const userProfile = item.user_profiles as any;
return {
user_id: userProfile.user_id,
bonus_points: item.bonus_points,
event_points: item.event_points,
first_name: userProfile.first_name,
last_name: userProfile.last_name,
};
}),
},
};
return returnData;

try {
const res = await axios.get(
`/api/battlepass/leaderboard?pageNumber=${pageNum}&pageSize=${pageSize}`
);

return res.data;
} catch {
throw new Error('Failed to fetch leaderboard');
}
}

async getUserRankLeaderboard(
userId: string
): Promise<{ data: { place: number } }> {
if (this.mock) return { data: { place: faker.datatype.number() } };
const leaderboardRes = await this.getLeaderboard(10000, 1);
const userFoundIndex = leaderboardRes.data.leaderboard.findIndex(
(user) => user.user_id == userId
);
return { data: { place: userFoundIndex + 1 } };

try {
const res = await axios.get(`/api/battlepass/user-rank/${userId}`);

return res.data;
} catch {
throw new Error('Failed to get user rank');
}
}

async getBonusPointEventsUserStatus(userId: string): Promise<{
Expand Down Expand Up @@ -266,20 +253,13 @@ export class BattlepassAPI implements BattlepassAPIInterface {
data: { points: faker.datatype.number() },
};
}
const userLeaderboardRes = await this.client
.from('leaderboard')
.select('bonus_points,event_points')
.eq('user_id', userId)
.single();
if (userLeaderboardRes.error) {
throw userLeaderboardRes.error;

try {
const res = await axios.get(`/api/battlepass/user-points/${userId}`);

return res.data;
} catch {
throw new Error('Failed to get user rank');
}
return {
data: {
points:
userLeaderboardRes.data.bonus_points +
userLeaderboardRes.data.event_points,
},
};
}
}
209 changes: 209 additions & 0 deletions apps/dashboard/common/events.utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
import { getEnv } from '@hibiscus/env';
import axios, { AxiosError } from 'axios';

// Type definitions

export type Event = {
eventId: string;
eventName: string;
startTime: Date;
endTime: Date;
location: string;
description?: string;
eventTags?: string[];
industryTags?: string[];
bpPoints: number;
};

export class EventServiceError extends Error {
constructor(message?: string) {
super(message ?? 'Unknown error occured');
}

static handleAxiosError(e: AxiosError): EventServiceError {
if (
e.response &&
typeof e.response.data === 'object' &&
'error' in e.response.data &&
typeof e.response.data.error === 'string'
) {
return new EventServiceError(e.response.data.error);
} else if (e.response) {
return new EventServiceError('Unknown API error occured');
} else if (e.request) {
return new EventServiceError(
'The request was made but no response was received'
);
} else {
return new EventServiceError(e.message);
}
}
}

// Util functions
export function isSameDate(date1: Date, date2: Date): boolean {
return (
date1.getFullYear() === date2.getFullYear() &&
date1.getMonth() === date2.getMonth() &&
date1.getDate() === date2.getDate()
);
}

export function getDayDate(d: Date): Date {
return new Date(d.getFullYear(), d.getMonth(), d.getDate());
}

// Event service API client
export async function getEvent(
eventId: string,
accessToken: string
): Promise<Event> {
const apiUrl = getEnv().Hibiscus.Events.ApiUrl;

try {
const res = await axios(`${apiUrl}/events/${eventId}`, {
method: 'GET',
headers: { Authorization: `Bearer ${accessToken}` },
});
const event = res.data;
event.startTime = new Date(event.startTime);
event.endTime = new Date(event.endTime);
return event;
} catch (e) {
if (axios.isAxiosError(e)) {
throw EventServiceError.handleAxiosError(e);
} else {
throw new EventServiceError();
}
}
}

export async function getAllEvents(accessToken: string): Promise<Event[]> {
const apiUrl = getEnv().Hibiscus.Events.ApiUrl;

// Display all events after Sep 1 2023
const startDate = new Date(2023, 8, 1).toISOString();

try {
const res = await axios(`${apiUrl}/events?after=${startDate}&pageSize=-1`, {
method: 'GET',
headers: { Authorization: `Bearer ${accessToken}` },
});
const events = res.data.events;
for (const e of events) {
e.startTime = new Date(e.startTime);
e.endTime = new Date(e.endTime);
}
return events;
} catch (e) {
if (axios.isAxiosError(e)) {
throw EventServiceError.handleAxiosError(e);
} else {
throw new EventServiceError();
}
}
}

export async function getPinnedEvents(
userId: string,
accessToken: string
): Promise<Event[]> {
const apiUrl = getEnv().Hibiscus.Events.ApiUrl;

try {
const res = await axios(`${apiUrl}/events/pinned-events/${userId}`, {
method: 'GET',
headers: { Authorization: `Bearer ${accessToken}` },
});
const events = res.data.pinnedEvents;
for (const e of events) {
e.startTime = new Date(e.startTime);
e.endTime = new Date(e.endTime);
}
return events;
} catch (e) {
if (axios.isAxiosError(e)) {
throw EventServiceError.handleAxiosError(e);
} else {
throw new EventServiceError();
}
}
}

export async function pinEvent(
userId: string,
eventId: string,
accessToken: string
): Promise<number> {
const apiUrl = getEnv().Hibiscus.Events.ApiUrl;

try {
const res = await axios(`${apiUrl}/events/pinned-events/${userId}`, {
method: 'POST',
headers: { Authorization: `Bearer ${accessToken}` },
data: {
pin_event: eventId,
},
});
const pinned = res.data.pinned_event;
return pinned;
} catch (e) {
if (axios.isAxiosError(e)) {
throw EventServiceError.handleAxiosError(e);
} else {
throw new EventServiceError();
}
}
}

export async function unpinEvent(
userId: string,
eventId: string,
accessToken: string
): Promise<number> {
const apiUrl = getEnv().Hibiscus.Events.ApiUrl;

try {
const res = await axios(`${apiUrl}/events/pinned-events/${userId}`, {
method: 'DELETE',
headers: { Authorization: `Bearer ${accessToken}` },
data: {
unpin_event: eventId,
},
});
const unpinned = res.data.unpinned_event;
return unpinned;
} catch (e) {
if (axios.isAxiosError(e)) {
throw EventServiceError.handleAxiosError(e);
} else {
throw new EventServiceError();
}
}
}

export async function updateEvent(
eventId: string,
props: Partial<Event>,
accessToken: string
): Promise<Event> {
const apiUrl = getEnv().Hibiscus.Events.ApiUrl;

try {
const res = await axios(`${apiUrl}/events/${eventId}`, {
method: 'PUT',
headers: { Authorization: `Bearer ${accessToken}` },
data: {
...props,
},
});
const event = res.data;
return event;
} catch (e) {
if (axios.isAxiosError(e)) {
throw EventServiceError.handleAxiosError(e);
} else {
throw new EventServiceError();
}
}
}
Loading
Loading