Skip to content

Commit

Permalink
Merge pull request #92 from uatisdeproblem/development
Browse files Browse the repository at this point in the history
Development
  • Loading branch information
uatisdeproblem committed Apr 11, 2024
2 parents 3067c29 + 705aad8 commit 8f4d69e
Show file tree
Hide file tree
Showing 51 changed files with 1,193 additions and 1,083 deletions.
4 changes: 4 additions & 0 deletions back-end/deploy/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ const apiResources: ResourceController[] = [
paths: ['/topics/{topicId}/messages/{messageId}/upvotes', '/topics/{topicId}/messages/{messageId}/upvotes/{userId}']
},
{ name: 'badges', paths: ['/badges', '/badges/{badge}'] },
{ name: 'usersBadges', paths: ['/usersBadges', '/usersBadges/{badge}'] },
{ name: 'usefulLinks', paths: ['/usefulLinks', '/usefulLinks/{linkId}'] },
{ name: 'deadlines', paths: ['/deadlines', '/deadlines/{deadlineId}'] },
{ name: 'communications', paths: ['/communications', '/communications/{communicationId}'] },
Expand Down Expand Up @@ -167,6 +168,9 @@ const tables: { [tableName: string]: DDBTable } = {
}
]
},
badges: {
PK: { name: 'badgeId', type: DDB.AttributeType.STRING }
},
usersBadges: {
PK: { name: 'userId', type: DDB.AttributeType.STRING },
SK: { name: 'badge', type: DDB.AttributeType.STRING }
Expand Down
4 changes: 2 additions & 2 deletions back-end/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion back-end/package.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"version": "1.8.7",
"version": "1.9.0",
"name": "back-end",
"scripts": {
"lint": "eslint --ext .ts",
Expand Down
6 changes: 3 additions & 3 deletions back-end/src/handlers/answersClaps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@

import { DynamoDB, HandledError, ResourceController } from 'idea-aws';

import { addBadgeToUser } from './badges';
import { addBadgeToUser } from './usersBadges';

import { Topic, TopicTypes } from '../models/topic.model';
import { Question } from '../models/question.model';
import { Answer, AnswerClap } from '../models/answer.model';
import { User } from '../models/user.model';
import { Badges } from '../models/userBadge.model';
import { BuiltInBadges } from '../models/badge.model';
import { Subject } from '../models/subject.model';

///
Expand Down Expand Up @@ -102,7 +102,7 @@ class AnswersClaps extends ResourceController {
await ddb.put({ TableName: DDB_TABLES.questions, Item: this.question });

if ((await this.getNumAnswersClappedByUser()) >= 15)
await addBadgeToUser(ddb, this.galaxyUser.userId, Badges.CHEERGIVER);
await addBadgeToUser(ddb, this.galaxyUser.userId, BuiltInBadges.CHEERGIVER);
}

protected async deleteResources(): Promise<void> {
Expand Down
3 changes: 2 additions & 1 deletion back-end/src/handlers/applications.ts
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,8 @@ class ApplicationsRC extends ResourceController {
const userId = this.opportunity.canUserManage(this.galaxyUser) ? this.application.userId : this.galaxyUser.userId;

const key = `${S3_ATTACHMENTS_FOLDER}/${attachment.attachmentId}-${userId}`;
return await s3.signedURLGet(S3_BUCKET_MEDIA, key);
const filename = attachment.name.concat('.', attachment.format);
return await s3.signedURLGet(S3_BUCKET_MEDIA, key, { filename });
}
private async reviewApplication(approve: boolean, message: string): Promise<Application> {
if (this.opportunity.isArchived()) throw new HandledError('Opportunity is archived');
Expand Down
94 changes: 40 additions & 54 deletions back-end/src/handlers/badges.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,14 @@
import { DynamoDB, HandledError, ResourceController } from 'idea-aws';

import { User } from '../models/user.model';
import { UserBadge, Badges } from '../models/userBadge.model';
import { Badge } from '../models/badge.model';

///
/// CONSTANTS, ENVIRONMENT VARIABLES, HANDLER
///

const DDB_TABLES = { usersBadges: process.env.DDB_TABLE_usersBadges };
const PROJECT = process.env.PROJECT;
const DDB_TABLES = { badges: process.env.DDB_TABLE_badges };
const ddb = new DynamoDB();

export const handler = (ev: any, _: any, cb: any): Promise<void> => new BadgesRC(ev, cb).handleRequest();
Expand All @@ -22,82 +23,67 @@ export const handler = (ev: any, _: any, cb: any): Promise<void> => new BadgesRC

class BadgesRC extends ResourceController {
galaxyUser: User;
userBadge: UserBadge;
badge: Badge;

constructor(event: any, callback: any) {
super(event, callback, { resourceId: 'badge' });
this.galaxyUser = new User(event.requestContext.authorizer.lambda.user);
}

protected async getResources(): Promise<UserBadge[]> {
const userId =
this.queryParams.userId && this.galaxyUser.isAdministrator ? this.queryParams.userId : this.galaxyUser.userId;
let usersBadges: UserBadge[] = await ddb.query({
TableName: DDB_TABLES.usersBadges,
KeyConditionExpression: 'userId = :userId',
ExpressionAttributeValues: { ':userId': userId }
});
usersBadges = usersBadges.map(x => new UserBadge(x));
return usersBadges.sort((a, b): number => b.earnedAt.localeCompare(a.earnedAt));
}
protected async checkAuthBeforeRequest(): Promise<void> {
if (!this.resourceId) return;

protected async getResource(): Promise<UserBadge> {
try {
this.userBadge = new UserBadge(
await ddb.get({
TableName: DDB_TABLES.usersBadges,
Key: { userId: this.galaxyUser.userId, badge: this.resourceId }
})
);
this.badge = new Badge(await ddb.get({ TableName: DDB_TABLES.badges, Key: { badgeId: this.resourceId } }));
} catch (err) {
throw new HandledError('Badge not found');
}
}

if (!this.userBadge.firstSeenAt) {
this.userBadge.firstSeenAt = new Date().toISOString();
await ddb.update({
TableName: DDB_TABLES.usersBadges,
Key: { userId: this.galaxyUser.userId, badge: this.resourceId },
UpdateExpression: 'SET firstSeenAt = :firstSeenAt',
ExpressionAttributeValues: { ':firstSeenAt': this.userBadge.firstSeenAt }
});
}
return this.userBadge;
protected async getResources(): Promise<Badge[]> {
const badges = (await ddb.scan({ TableName: DDB_TABLES.badges })).map(b => new Badge(b));
return badges.sort((a, b): number => a.name.localeCompare(b.name));
}

protected async postResource(): Promise<void> {
protected async getResource(): Promise<Badge> {
return this.badge;
}

protected async postResources(): Promise<Badge> {
if (!this.galaxyUser.isAdministrator) throw new HandledError('Unauthorized');

const { userId } = this.queryParams;
const badge = this.resourceId as Badges;
this.badge = new Badge(this.body);
this.badge.badgeId = await ddb.IUNID(PROJECT);
if (this.badge.isBuiltIn()) throw new HandledError('Built-in badges cannot be created');

if (!userId) throw new HandledError('No target user');
if (!badge || !Object.values(Badges).includes(badge)) throw new HandledError('Invalid badge');
await this.putSafeResource({ noOverwrite: true });

await addBadgeToUser(ddb, userId, badge);
return this.badge;
}

protected async deleteResource(): Promise<void> {
protected async putResource(): Promise<Badge> {
if (!this.galaxyUser.isAdministrator) throw new HandledError('Unauthorized');

const { userId } = this.queryParams;
const badge = this.resourceId as Badges;
const oldBadge = new Badge(this.badge);
this.badge.safeLoad(this.body, oldBadge);

return await this.putSafeResource({ noOverwrite: false });
}

if (!userId) throw new HandledError('No target user');
protected async deleteResource(): Promise<void> {
if (!this.galaxyUser.isAdministrator) throw new HandledError('Unauthorized');

await ddb.delete({ TableName: DDB_TABLES.usersBadges, Key: { userId, badge } });
await ddb.delete({ TableName: DDB_TABLES.badges, Key: { badgeId: this.resourceId } });
}
}

export const addBadgeToUser = async (ddb: DynamoDB, userId: string, badge: Badges): Promise<void> => {
try {
const userBadge = new UserBadge({ userId, badge });
await ddb.put({
TableName: DDB_TABLES.usersBadges,
Item: userBadge,
ConditionExpression: 'attribute_not_exists(userId) AND attribute_not_exists(badge)'
});
} catch (error) {
// user already has the badge
private async putSafeResource(opts: { noOverwrite: boolean }): Promise<Badge> {
const errors = this.badge.validate();
if (errors.length) throw new HandledError(`Invalid fields: ${errors.join(', ')}`);

const putParams: any = { TableName: DDB_TABLES.badges, Item: this.badge };
if (opts.noOverwrite) putParams.ConditionExpression = 'attribute_not_exists(badgeId)';
await ddb.put(putParams);

return this.badge;
}
};
}
8 changes: 4 additions & 4 deletions back-end/src/handlers/messagesUpvotes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@

import { DynamoDB, HandledError, ResourceController } from 'idea-aws';

import { addBadgeToUser } from './badges';
import { addBadgeToUser } from './usersBadges';

import { Topic, TopicTypes } from '../models/topic.model';
import { Message, MessageUpvote } from '../models/message.model';
import { User } from '../models/user.model';
import { Badges } from '../models/userBadge.model';
import { BuiltInBadges } from '../models/badge.model';
import { Subject } from '../models/subject.model';

///
Expand Down Expand Up @@ -87,9 +87,9 @@ class MessagesUpvotesRC extends ResourceController {
this.message.numOfUpvotes = await this.getLiveNumUpvotes();
await ddb.put({ TableName: DDB_TABLES.messages, Item: this.message });

await addBadgeToUser(ddb, this.galaxyUser.userId, Badges.NEWCOMER);
await addBadgeToUser(ddb, this.galaxyUser.userId, BuiltInBadges.NEWCOMER);
if ((await this.getNumMessagesUpvotedByUser()) >= 15)
await addBadgeToUser(ddb, this.galaxyUser.userId, Badges.LOVE_GIVER);
await addBadgeToUser(ddb, this.galaxyUser.userId, BuiltInBadges.LOVE_GIVER);
}

protected async deleteResources(): Promise<void> {
Expand Down
5 changes: 3 additions & 2 deletions back-end/src/handlers/publicAttachments.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,12 @@ class PublicAttachmentsRC extends ResourceController {
return signedURL;
}
private async getSignedURLToDownloadAttachment(): Promise<SignedURL> {
const { attachmentId } = this.body;
const { attachmentId, filename } = this.body;
if (!attachmentId) throw new HandledError('Missing attachment ID');
if (!attachmentId.startsWith(ATTACHMENTS_PREFIX)) throw new HandledError('Not found');

const key = `${S3_ATTACHMENTS_FOLDER}/${attachmentId}`;
return await s3.signedURLGet(S3_BUCKET_MEDIA, key);
const options = filename ? { filename } : {};
return await s3.signedURLGet(S3_BUCKET_MEDIA, key, options);
}
}
8 changes: 4 additions & 4 deletions back-end/src/handlers/questions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@
import { DynamoDB, HandledError, ResourceController, SES } from 'idea-aws';

import { isEmailInBlockList } from './sesNotifications';
import { addBadgeToUser } from './badges';
import { addBadgeToUser } from './usersBadges';

import { Topic, TopicTypes } from '../models/topic.model';
import { Question } from '../models/question.model';
import { Answer } from '../models/answer.model';
import { User } from '../models/user.model';
import { Badges } from '../models/userBadge.model';
import { BuiltInBadges } from '../models/badge.model';
import { Subject } from '../models/subject.model';
import { Configurations } from '../models/configurations.model';

Expand Down Expand Up @@ -122,9 +122,9 @@ class Questions extends ResourceController {

await this.sendNotificationToTopicSubjects(this.topic, this.question);

await addBadgeToUser(ddb, this.galaxyUser.userId, Badges.FIRST_QUESTION);
await addBadgeToUser(ddb, this.galaxyUser.userId, BuiltInBadges.FIRST_QUESTION);
if ((await this.getNumQuestionsMadeByUser()) >= 10)
await addBadgeToUser(ddb, this.galaxyUser.userId, Badges.QUESTIONS_MASTER);
await addBadgeToUser(ddb, this.galaxyUser.userId, BuiltInBadges.QUESTIONS_MASTER);

return this.question;
}
Expand Down
8 changes: 4 additions & 4 deletions back-end/src/handlers/questionsUpvotes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@

import { DynamoDB, HandledError, ResourceController } from 'idea-aws';

import { addBadgeToUser } from './badges';
import { addBadgeToUser } from './usersBadges';

import { Topic, TopicTypes } from '../models/topic.model';
import { Question, QuestionUpvote } from '../models/question.model';
import { User } from '../models/user.model';
import { Badges } from '../models/userBadge.model';
import { BuiltInBadges } from '../models/badge.model';
import { Subject } from '../models/subject.model';

///
Expand Down Expand Up @@ -86,9 +86,9 @@ class QuestionsUpvotesRC extends ResourceController {
this.question.numOfUpvotes = await this.getLiveNumUpvotes();
await ddb.put({ TableName: DDB_TABLES.questions, Item: this.question });

await addBadgeToUser(ddb, this.galaxyUser.userId, Badges.NEWCOMER);
await addBadgeToUser(ddb, this.galaxyUser.userId, BuiltInBadges.NEWCOMER);
if ((await this.getNumQuestionsUpvotedByUser()) >= 15)
await addBadgeToUser(ddb, this.galaxyUser.userId, Badges.LOVE_GIVER);
await addBadgeToUser(ddb, this.galaxyUser.userId, BuiltInBadges.LOVE_GIVER);
}

protected async deleteResources(): Promise<void> {
Expand Down
8 changes: 4 additions & 4 deletions back-end/src/handlers/topics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@
import { DynamoDB, HandledError, ResourceController, S3 } from 'idea-aws';
import { Attachment } from 'idea-toolbox';

import { addBadgeToUser } from './badges';
import { addBadgeToUser } from './usersBadges';
import { addStatisticEntry } from './statistics';

import { TopicCategoryAttached } from '../models/category.model';
import { GAEventAttached } from '../models/event.model';
import { Topic, TopicTypes } from '../models/topic.model';
import { RelatedTopic } from '../models/relatedTopic.model';
import { User } from '../models/user.model';
import { Badges } from '../models/userBadge.model';
import { BuiltInBadges } from '../models/badge.model';
import { SubjectTypes } from '../models/subject.model';
import { StatisticEntityTypes } from '../models/statistic.model';
import { Application } from '../models/application.model';
Expand Down Expand Up @@ -123,7 +123,7 @@ class Topics extends ResourceController {
await this.putSafeResource({ noOverwrite: true });

const userSubjects = this.topic.subjects.filter(s => s.type === SubjectTypes.USER);
for (const user of userSubjects) await addBadgeToUser(ddb, user.id, Badges.RISING_STAR);
for (const user of userSubjects) await addBadgeToUser(ddb, user.id, BuiltInBadges.RISING_STAR);

return this.topic;
}
Expand Down Expand Up @@ -184,7 +184,7 @@ class Topics extends ResourceController {

await this.putSafeResource({ noOverwrite: true });

await addBadgeToUser(ddb, application.subject.id, Badges.RISING_STAR);
await addBadgeToUser(ddb, application.subject.id, BuiltInBadges.RISING_STAR);

return this.topic;
}
Expand Down
Loading

0 comments on commit 8f4d69e

Please sign in to comment.