Skip to content

Ben #76

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

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
Open

Ben #76

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
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,12 @@ Thumbs.db
.vscode/
.idea/

# Enviroment
.env

# Build output
dist/
build/

# Logs
*.log
12 changes: 12 additions & 0 deletions backend/dist/auth/auth.controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ let AuthController = class AuthController {
async setNewPassword(newPassword, session, username, email) {
return await this.authService.setNewPassword(newPassword, session, username, email);
}
async updateProfile(username, displayName) {
await this.authService.updateProfile(username, displayName);
return { message: 'Profile has been updated' };
}
};
__decorate([
(0, common_1.Post)('register'),
Expand Down Expand Up @@ -58,6 +62,14 @@ __decorate([
__metadata("design:paramtypes", [String, String, String, String]),
__metadata("design:returntype", Promise)
], AuthController.prototype, "setNewPassword", null);
__decorate([
(0, common_1.Post)('update-profile'),
__param(0, (0, common_1.Body)('username')),
__param(1, (0, common_1.Body)('displayName')),
__metadata("design:type", Function),
__metadata("design:paramtypes", [String, String]),
__metadata("design:returntype", Promise)
], AuthController.prototype, "updateProfile", null);
AuthController = __decorate([
(0, common_1.Controller)('auth'),
__metadata("design:paramtypes", [auth_service_1.AuthService])
Expand Down
22 changes: 22 additions & 0 deletions backend/dist/auth/auth.service.js
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,28 @@ let AuthService = AuthService_1 = class AuthService {
throw new Error('An unknown error occurred');
}
}
async updateProfile(username, displayName) {
try {
const tableName = process.env.DYNAMODB_USER_TABLE_NAME || 'TABLE_FAILURE';
const params = {
TableName: tableName,
Key: { userId: username },
UpdateExpression: 'set displayName = :displayName',
ExpressionAttributeValues: {
':displayName': displayName
},
};
await this.dynamoDb.update(params).promise();
this.logger.log(`User ${username} updated user profile.`);
}
catch (error) {
if (error instanceof Error) {
this.logger.error('Updating the profile failed', error.stack);
throw new Error(error.message || 'Updating the profile failed');
}
throw new Error('An unknown error occurred');
}
}
};
AuthService = AuthService_1 = __decorate([
(0, common_1.Injectable)()
Expand Down
2 changes: 2 additions & 0 deletions backend/dist/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,15 @@ const core_1 = require("@nestjs/core");
const app_module_1 = require("./app.module");
const dotenv = __importStar(require("dotenv"));
const aws_sdk_1 = __importDefault(require("aws-sdk"));
const common_1 = require("@nestjs/common");
/* ! */
async function bootstrap() {
aws_sdk_1.default.config.update({
region: process.env.AWS_REGION
});
const app = await core_1.NestFactory.create(app_module_1.AppModule);
app.enableCors();
app.useGlobalPipes(new common_1.ValidationPipe());
await app.listen(3001);
}
dotenv.config();
Expand Down
40 changes: 40 additions & 0 deletions backend/package-lock.json

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

2 changes: 2 additions & 0 deletions backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
"@nestjs/passport": "^8.0.0",
"@nestjs/platform-express": "^8.4.7",
"aws-sdk": "^2.1030.0",
"class-transformer": "^0.5.1",
"class-validator": "^0.14.1",
"crypto": "^1.0.1",
"dotenv": "^16.4.5",
"jwt-decode": "^4.0.0",
Expand Down
56 changes: 56 additions & 0 deletions backend/src/grant/customValidators.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import {
ValidatorConstraint,
ValidatorConstraintInterface,
ValidationArguments,
registerDecorator,
ValidationOptions,
validate,
} from 'class-validator';

// Custom validator logic
@ValidatorConstraint({ name: 'isEmailOrPhoneFormat', async: false })
export class IsEmailOrPhoneFormatConstraint implements ValidatorConstraintInterface {
private async isEmail(value: string): Promise<boolean> {
// Use the built-in IsEmail validator as a function
const errors = await validate({ email: value.replace('Email: ', '') }, { skipMissingProperties: true });
return errors.length === 0; // No errors means the email is valid
}

private isPhoneNumber(value: string): boolean {
// Simple check for "Phone Number: " prefix and a valid phone number format
if (!value.startsWith('Phone Number: ')) return false;
const phoneNumber = value.replace('Phone Number: ', '');
// Example: Validate phone number format (adjust as needed)
return /^\+\d{1,3} \d{3,14}$/.test(phoneNumber); // Example: +1 1234567890
}

async validate(value: string, args: ValidationArguments) {
if(!value || value == undefined){
return false
}

if (value.startsWith('Email: ')) {
return this.isEmail(value);
} else if (value.startsWith('Phone Number: ')) {
return this.isPhoneNumber(value);
}
return false; // Invalid format
}

defaultMessage(args: ValidationArguments) {
return `The point of contact value must be in the format "Email: <email>" or "Phone Number: <phone number>"`;
}
}

// Decorator for easier use
export function IsPointOfContact(validationOptions?: ValidationOptions) {
return function (object: Object, propertyName: string) {
registerDecorator({
target: object.constructor,
propertyName: propertyName,
options: validationOptions,
constraints: [],
validator: IsEmailOrPhoneFormatConstraint,
});
};
}
74 changes: 74 additions & 0 deletions backend/src/grant/dto/grant.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import {
IsNumber,
IsString,
IsBoolean,
IsArray,
IsISO8601,
IsNotEmpty,
IsDefined,
} from "class-validator";
import { IsPointOfContact } from "../customValidators";

// Tried just doing this without the ! definite statement but i got errors
// From my research definite statement just says this starts as undefined but the program will treat it as the type you said
export class CreateGrantDto {
@IsString()
@IsNotEmpty()
@IsDefined()
organization_name!: string;

@IsString()
@IsNotEmpty()
@IsDefined()
description!: string;

@IsBoolean()
@IsNotEmpty()
@IsDefined()
is_bcan_qualifying!: boolean;

@IsString()
@IsNotEmpty()
@IsDefined()
status!: boolean;

@IsNumber()
@IsNotEmpty()
@IsDefined()
amount!: number;

@IsISO8601()
@IsNotEmpty()
@IsDefined()
deadline!: string;

@IsBoolean()
@IsNotEmpty()
@IsDefined()
notifications_on_for_user!: boolean;

@IsString()
@IsDefined()
reporting_requirements!: string;

@IsString()
@IsDefined()
restrictions!: string;

@IsArray()
@IsDefined()
@IsDefined({each: true})
@IsPointOfContact({ each: true })
point_of_contacts!: string[];

@IsArray()
@IsString({ each: true })
@IsDefined()
@IsNotEmpty({each: true})
attached_resources!: string[];

@IsString()
@IsNotEmpty()
@IsDefined()
comments!: string;
}
59 changes: 46 additions & 13 deletions backend/src/grant/grant.controller.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,51 @@
import { Controller, Get, Param, Query } from '@nestjs/common';
import { GrantService } from './grant.service';
import {
Controller,
Get,
Param,
Put,
Body,
Post,
ValidationPipe,
} from "@nestjs/common";
import { GrantService } from "./grant.service";
import { CreateGrantDto } from "./dto/grant.dto";

@Controller('grant')
@Controller("grant")
export class GrantController {
constructor(private readonly grantService: GrantService) { }
constructor(private readonly grantService: GrantService) {}

@Get()
async getAllGrants() {
return await this.grantService.getAllGrants();
}
@Get()
async getAllGrants() {
return await this.grantService.getAllGrants();
}

@Get(':id')
async getGrantById(@Param('id') GrantId: string) {
return await this.grantService.getGrantById(parseInt(GrantId, 10));
}
@Get(":id")
async getGrantById(@Param("id") GrantId: string) {
console.log("getting grant by id")
return await this.grantService.getGrantById(parseInt(GrantId, 10));
}

}
@Put("archive")
async archive(@Body("grantIds") grantIds: number[]): Promise<number[]> {
return await this.grantService.unarchiveGrants(grantIds);
}

@Put("unarchive")
async unarchive(@Body("grantIds") grantIds: number[]): Promise<number[]> {
return await this.grantService.unarchiveGrants(grantIds);
}

@Post("new-grant")
async addGrant(
@Body(
new ValidationPipe({
whitelist: true,
forbidNonWhitelisted: true,
})
)
grant: CreateGrantDto
) {
console.log(grant);
return await this.grantService.addGrant(grant);
}
}
7 changes: 4 additions & 3 deletions backend/src/grant/grant.model.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
// model for Grant objects
export interface Grant {
export default interface Grant {
grantId: number;
organization_name: string;
description: string;
is_bcan_qualifying: boolean;
status: string;
amount: number;
deadline: string; // dynamo does not have a built-in Date type
deadline: string; // dynamo does not have a built-in Date type and will be in iso8601
notifications_on_for_user: boolean;
reporting_requirements: string;
restrictions: string;
point_of_contacts: string[];
attached_resources: string[];
comments: string[];
}
}

Loading