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

fix: dashboard various fixes #78

Merged
merged 33 commits into from
May 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
bc5e38c
Add joined at date for searched user
adelinaenache May 3, 2024
ffe6c3a
Update user search query to respect respect http standards
adelinaenache May 3, 2024
114a081
Correct relations
adelinaenache May 3, 2024
0832ac3
Check if users are connected when doing an user search
adelinaenache May 3, 2024
0d875d1
fix db structure
adelinaenache May 9, 2024
2299023
Fix: is connection should be true when userId is in the followers
adelinaenache May 13, 2024
5c00a65
Do not use mutler for uploading profile pics
adelinaenache May 17, 2024
cb1d5db
update review properties
adelinaenache May 17, 2024
8a0ad34
add get user endpoint
adelinaenache May 18, 2024
d2476fe
use headline insted of jobtitle
adelinaenache May 18, 2024
f4bd076
remove console logs
adelinaenache May 20, 2024
4929924
remove console log
adelinaenache May 20, 2024
d20e210
Move reviwes in dedicated controller.
adelinaenache May 21, 2024
618327c
enable swagger plugin
adelinaenache May 21, 2024
edd1887
review db structure changes:
adelinaenache May 21, 2024
f49e0be
Add ability to like/unlike reviews and review state
adelinaenache May 21, 2024
33fdccc
add ability to modify and update own review
adelinaenache May 21, 2024
accdd12
feat: add connections controller and move specific endpoints from use…
adelinaenache May 22, 2024
8f6d6d8
fix: review annonymous property
adelinaenache May 22, 2024
54011ba
fix: add isEmailVerified to connection
adelinaenache May 22, 2024
859906c
fix rebase errors
adelinaenache May 22, 2024
0854c72
Move search by profile url in connections.
adelinaenache May 22, 2024
dfea6a6
Return transformed user when social account is already existen
adelinaenache May 23, 2024
e7bf445
add logging
adelinaenache May 23, 2024
1b1966d
fix reviews issues
adelinaenache May 23, 2024
f4612b4
rename dto folder
adelinaenache May 24, 2024
0fa2298
rename methods
adelinaenache May 24, 2024
47a2e7f
make only one DB query for craeting a connection
adelinaenache May 24, 2024
c6d93b3
change DTO folder to a random name (because gh is not case sensitive …
adelinaenache May 24, 2024
b7c5ab1
rename dto folder
adelinaenache May 24, 2024
952eb25
Merge branch 'develop' into dashboard-various-fixes
adelinaenache May 24, 2024
05753e7
squash migrations
adelinaenache May 24, 2024
969b532
fix tests
adelinaenache May 24, 2024
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
6 changes: 5 additions & 1 deletion jest.config.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@
"testEnvironment": "node",
"testPathIgnorePatterns": [".*.e2e.spec.ts"],
"transform": {
"^.+\\.[tj]s$": ["ts-jest", { "tsconfig": "<rootDir>/tsconfig.spec.json" }]
"^.+\\.[tj]s$": ["ts-jest", { "tsconfig": "<rootDir>/tsconfig.spec.json" },
{ "astTransformers": {
"before": ["./utils/e2e-plugin.ts"]
}
}]
},
"moduleFileExtensions": ["ts", "js", "html"],
"coverageDirectory": "./coverage-e2e"
Expand Down
6 changes: 5 additions & 1 deletion jest.e2e-config.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@
"testEnvironment": "node",
"testMatch": ["**/*.e2e.spec.ts"],
"transform": {
"^.+\\.[tj]s$": ["ts-jest", { "tsconfig": "<rootDir>/tsconfig.spec.json" }]
"^.+\\.[tj]s$": ["ts-jest", { "tsconfig": "<rootDir>/tsconfig.spec.json" },
{ "astTransformers": {
"before": ["./utils/e2e-plugin.ts"]
}
}]
},
"moduleFileExtensions": ["ts", "js", "html"],
"coverageDirectory": "./coverage-e2e"
Expand Down
11 changes: 10 additions & 1 deletion nest-cli.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,15 @@
"collection": "@nestjs/schematics",
"sourceRoot": "src",
"compilerOptions": {
"deleteOutDir": true
"deleteOutDir": true,
"plugins":[
{
"name": "@nestjs/swagger",
"options": {
"classValidatorShim": false,
"introspectComments": true
}
}
]
}
}
17 changes: 0 additions & 17 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -88,22 +88,5 @@
"ts-node": "^10.9.2",
"tsconfig-paths": "^4.2.0",
"typescript": "^5.1.3"
},
"jest": {
"moduleFileExtensions": [
"js",
"json",
"ts"
],
"rootDir": "src",
"testRegex": ".*\\.spec\\.ts$",
"transform": {
"^.+\\.(t|j)s$": "ts-jest"
},
"collectCoverageFrom": [
"**/*.(t|j)s"
],
"coverageDirectory": "../coverage",
"testEnvironment": "node"
}
}
13 changes: 11 additions & 2 deletions src/app/app.module.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Module } from '@nestjs/common';
import { MiddlewareConsumer, Module, NestModule } from '@nestjs/common';
import { AppController } from './app.controller';
import { AuthModule } from '../auth/auth.module';
import { UserModule } from '../user/user.module';
Expand All @@ -9,6 +9,9 @@ import { AuthGuard } from '../auth/guard/auth/auth.guard';
import { PrismaModule } from '../prisma/prisma.module';
import { MailModule } from '../mail/mail.module';
import { ProviderModule } from '../provider/provider.module';
import { ReviewsModule } from '../../src/reviews/reviews.module';
import { ConnectionsModule } from '../../src/connections/connections.module';
import { AppLoggerMiddleware } from '../../src/middlewares/logger.middleware';
import { CommonModule } from '../common/common.module';

@Module({
Expand All @@ -23,6 +26,8 @@ import { CommonModule } from '../common/common.module';
PrismaModule,
MailModule,
ProviderModule,
ReviewsModule,
ConnectionsModule,
],
controllers: [AppController],
providers: [
Expand All @@ -32,4 +37,8 @@ import { CommonModule } from '../common/common.module';
},
],
})
export class AppModule {}
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer): void {
consumer.apply(AppLoggerMiddleware).forRoutes('*');
}
}
21 changes: 21 additions & 0 deletions src/connections/connections.controller.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { Test, TestingModule } from '@nestjs/testing';
import { ConnectionsController } from './connections.controller';
import { ConnectionsService } from './connections.service';
import { PrismaService } from '../../src/prisma/prisma.service';

describe('ConnectionsController', () => {
let controller: ConnectionsController;

beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [ConnectionsController],
providers: [ConnectionsService, PrismaService],
}).compile();

controller = module.get<ConnectionsController>(ConnectionsController);
});

it('should be defined', () => {
expect(controller).toBeDefined();
});
});
97 changes: 97 additions & 0 deletions src/connections/connections.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import { Controller, Delete, Get, Param, Post } from '@nestjs/common';
import { CurrentUser } from '../../src/decorators/current-user.decorator';
import { ConnectionsService } from './connections.service';
import { ConnectionDto } from './dto/Connection.dto';
import {
ApiBadRequestResponse,
ApiBearerAuth,
ApiNotFoundResponse,
} from '@nestjs/swagger';
import { User } from '@prisma/client';
import { Public } from '../../src/decorators/public.decorator';

@ApiBearerAuth()
@Controller('connections')
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Our existing endpoints use singular in the controller name, so we should be sticking to connection in here.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a larger discussions. I think it makes sense to have user since in that controller we're usually dealing with a single user.
Put in reviews and connections we are usually dealing with multiple resources. See: Google's api design guidelines
and
this StackOverflow discussion

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This makes sense

export class ConnectionsController {
constructor(private connectionsService: ConnectionsService) {}
/**
*
* Get all of the current user's connections
*/
@Get()
async getAllConnections(@CurrentUser() user: User): Promise<ConnectionDto[]> {
return this.connectionsService.getUserConnections(user.id);
}

/**
* Get "suggested for review" connections.
*/
@Get('/suggested')
async getSuggestedConnections(
@CurrentUser() user: User,
): Promise<ConnectionDto[]> {
return this.connectionsService.getUserConnections(user.id);
}

/**
* Search an user by query.
*/
@Get('/search/:query')
@Public()
@ApiBadRequestResponse()
async searchUser(
@Param('query') query: string,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe you can add an alphanumeric regex check in here so that users cant use SQL injection?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If prisma wouldn't be Sql injection safe out of the box all of our endpoint would be vulnerable for such a thing since we're not doing any kind of prevention for this anywhere.
Glad that is safe out of the box >.<
https://stackoverflow.com/questions/73943274/prisma-findunique-is-sql-injection-safe

@CurrentUser() user?: User,
): Promise<ConnectionDto[]> {
return this.connectionsService.searchUsers(user?.id, query);
}

/**
* Create a connection between current User and another user.
*/
@Post('/connect/:userId')
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think a better name for this would be add. Similarly, for the delete endpoint it can be remove. This will look pretty meaningful when put together:

  • GET /api/connection/add/:userId
  • DELETE /api/connection/remove/:userId

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The way it is right now it is in alignment with RESTful standards. The verb "POST" signifies a resource creation, and DELETE signifies a resource removal.
Adding verbs like add and remove in the endpoint path can be redundant and not as clean as using HTTP methods to indicate the action. RESTful best practices suggest using nouns in URLs and verbs in HTTP methods.

https://mglaman.dev/blog/post-or-put-patch-and-delete-urls-are-cheap-api-design-matters
https://www.mscharhag.com/api-design/rest-deleting-resources

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

True, I know it is bit of a gamble in here. The thing is, feels that the connect word might specify an action and not a resource. Truly we can get rid of add and delete since the methods will signify that. I think we can go with /api/connection/:userId as the path and DELETE and POST as the methods?

async connectWithUser(
@CurrentUser() user: User,
@Param('userId') userId: string,
): Promise<ConnectionDto> {
return this.connectionsService.addConnection(user.id, userId);
}
/**
*
* Remove connection between current user and another user.
*/
@Delete('/connect/:userId')
async unconnectWithUser(
@CurrentUser() user: User,
@Param('userId') userId: string,
): Promise<ConnectionDto> {
return this.connectionsService.removeConnection(user.id, userId);
}

/**
* Search for users by external profile URL
* @param profileUrlBase64
*/

@Public()
@Get('search-by-external-profile/:profileUrl')
async searchUsersByExternalProfile(
@Param('profileUrl') profileUrlBase64: string,
) {
return this.connectionsService.searchUserByExternalProfile(
profileUrlBase64,
);
}

/**
* Get a connection.
*/
@Get('/:userId')
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not entirely sure about this usecase. Can you link a screenshot/url?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay!

@ApiNotFoundResponse()
async getUser(
@CurrentUser() user: User,
@Param('userId') userId: string,
): Promise<ConnectionDto> {
return this.connectionsService.getConnection(userId, user.id);
}
}
9 changes: 9 additions & 0 deletions src/connections/connections.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { Module } from '@nestjs/common';
import { ConnectionsService } from './connections.service';
import { ConnectionsController } from './connections.controller';

@Module({
providers: [ConnectionsService],
controllers: [ConnectionsController],
})
export class ConnectionsModule {}
19 changes: 19 additions & 0 deletions src/connections/connections.service.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { Test, TestingModule } from '@nestjs/testing';
import { ConnectionsService } from './connections.service';
import { PrismaService } from '../../src/prisma/prisma.service';

describe('ConnectionsService', () => {
let service: ConnectionsService;

beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [ConnectionsService, PrismaService],
}).compile();

service = module.get<ConnectionsService>(ConnectionsService);
});

it('should be defined', () => {
expect(service).toBeDefined();
});
});
Loading
Loading