Skip to content

Commit

Permalink
Merge pull request #527 from chaynHQ/develop
Browse files Browse the repository at this point in the history
Merge Develop onto Main
  • Loading branch information
eleanorreem authored Jul 23, 2024
2 parents 18608e0 + 2842fe3 commit b82ebff
Show file tree
Hide file tree
Showing 18 changed files with 600 additions and 227 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/.ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ jobs:
SIMPLYBOOK_CREDENTIALS: '{"login": "login"}'
STORYBLOK_WEBHOOK_SECRET: ${{ secrets.STORYBLOK_WEBHOOK_SECRET }}
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- name: Get yarn cache directory path
id: yarn-cache-dir-path
run: echo "::set-output name=dir::$(yarn cache dir)"
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/codeql-analysis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ jobs:

steps:
- name: Checkout repository
uses: actions/checkout@v3
uses: actions/checkout@v4

# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
Expand Down
10 changes: 5 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
"@nestjs/config": "^3.2.2",
"@nestjs/core": "^10.3.6",
"@nestjs/platform-express": "^10.3.7",
"@nestjs/swagger": "^7.3.1",
"@nestjs/swagger": "^7.4.0",
"@nestjs/terminus": "^10.2.3",
"@nestjs/typeorm": "^10.0.2",
"axios": "^1.7.2",
Expand All @@ -44,13 +44,13 @@
"date-fns": "^3.6.0",
"dotenv": "^16.4.5",
"firebase": "^10.10.0",
"firebase-admin": "^12.0.0",
"firebase-admin": "^12.2.0",
"lodash": "^4.17.21",
"newrelic": "^11.17.0",
"pg": "^8.11.5",
"pg-connection-string": "^2.6.4",
"reflect-metadata": "^0.2.1",
"rimraf": "^5.0.5",
"rimraf": "^6.0.1",
"rollbar": "^2.26.4",
"rxjs": "^7.8.1",
"storyblok-js-client": "^6.7.1",
Expand All @@ -74,12 +74,12 @@
"jest": "^29.7.0",
"prettier": "^3.2.5",
"supertest": "^7.0.0",
"ts-jest": "^29.2.2",
"ts-jest": "^29.2.3",
"ts-loader": "^9.5.1",
"ts-node": "^10.9.2",
"tsconfig-paths": "^4.2.0",
"typescript": "^5.4.5",
"typescript-eslint": "^7.6.0"
"typescript-eslint": "^7.16.1"
},
"engines": {
"node": "20.x",
Expand Down
23 changes: 23 additions & 0 deletions src/api/simplybook/simplybook-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,29 @@ export const deleteClient: (clientId: string) => Promise<string> = async (client
}
};

export const updateSimplybookClient = async (clientId: string, clientData: { email?: string }) => {
const token = await getAuthToken();
try {
const bookingsResponse = await axios.patch(`${SIMPLYBOOK_API_BASE_URL}/client/${clientId}`, {
headers: {
'Content-Type': 'application/json',
'X-Company-Login': simplybookCompanyName,
'X-Token': `${token}`,
},
body: clientData,
});
LOGGER.log({ event: 'UPDATE_SIMPLYBOOK_CLIENT', fields: [Object.keys(clientData)] });
return bookingsResponse.data.data;
} catch (error) {
LOGGER.error({
error: 'SIMPLYBOOK_CLIENT_UPDATE_ERROR',
status: error.status,
errorMessage: error.message,
});
handleError(`Failed to edit client ${clientId} from Simplybook.`, error);
}
};

const handleError = (error, message: string) => {
LOGGER.error(message, error);
throw new Error(`${message}: ${error})`);
Expand Down
2 changes: 2 additions & 0 deletions src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { PartnerAccessModule } from './partner-access/partner-access.module';
import { PartnerAdminModule } from './partner-admin/partner-admin.module';
import { PartnerFeatureModule } from './partner-feature/partner-feature.module';
import { PartnerModule } from './partner/partner.module';
import { SessionFeedbackModule } from './session-feedback/session-feedback.module';
import { SessionUserModule } from './session-user/session-user.module';
import { SessionModule } from './session/session.module';
import { SubscriptionUserModule } from './subscription-user/subscription-user.module';
Expand All @@ -35,6 +36,7 @@ import { WebhooksModule } from './webhooks/webhooks.module';
CourseModule,
CourseUserModule,
SessionUserModule,
SessionFeedbackModule,
CoursePartnerModule,
SubscriptionUserModule,
FeatureModule,
Expand Down
8 changes: 8 additions & 0 deletions src/partner/dtos/update-partner.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { IsBoolean } from 'class-validator';
import { ApiProperty } from '@nestjs/swagger';

export class UpdatePartnerDto {
@IsBoolean()
@ApiProperty({ type: Boolean })
active: boolean;
}
17 changes: 10 additions & 7 deletions src/partner/partner.controller.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { Body, Controller, Get, Param, Post, UseGuards } from '@nestjs/common';
import { Body, Controller, Get, Param, Patch, Post, UseGuards } from '@nestjs/common';
import { ApiBearerAuth, ApiBody, ApiOperation, ApiParam, ApiTags } from '@nestjs/swagger';
import { formatPartnerObject } from 'src/utils/serialize';
import { PartnerEntity } from '../entities/partner.entity';
import { SuperAdminAuthGuard } from '../partner-admin/super-admin-auth.guard';
import { ControllerDecorator } from '../utils/controller.decorator';
import { CreatePartnerDto } from './dtos/create-partner.dto';
import { DeletePartnerDto } from './dtos/delete-partner.dto';
import { IPartner } from './partner.interface';
import { PartnerService } from './partner.service';
import { UpdatePartnerDto } from './dtos/update-partner.dto';

@ApiTags('Partner')
@ControllerDecorator()
Expand Down Expand Up @@ -46,10 +46,13 @@ export class PartnerController {

@ApiBearerAuth('access-token')
@UseGuards(SuperAdminAuthGuard)
@Post('delete')
@ApiOperation({ description: 'Deletes a partner profile and makes partnerAccess inactive' })
@ApiBody({ type: DeletePartnerDto })
async deletePartner(@Body() deletePartnerDto: DeletePartnerDto) {
return this.partnerService.deletePartner(deletePartnerDto);
@Patch(':id')
@ApiOperation({ description: 'Update a partner profile and makes partner active or inactive' })
@ApiBody({ type: UpdatePartnerDto })
async updatePartner(
@Param() { id },
@Body() updatePartnerDto: UpdatePartnerDto,
) {
return this.partnerService.updatePartner(id, updatePartnerDto);
}
}
26 changes: 26 additions & 0 deletions src/partner/partner.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { PartnerAdminEntity } from '../entities/partner-admin.entity';
import { UserEntity } from '../entities/user.entity';
import { mockPartnerRepositoryMethods } from '../../test/utils/mockedServices';
import { mockPartnerEntity } from '../../test/utils/mockData';
import { createQueryBuilderMock } from '../../test/utils/mockUtils';

const createPartnerDto = {
name: mockPartnerEntity.name,
Expand Down Expand Up @@ -59,4 +60,29 @@ describe('PartnerService', () => {
await expect(service.createPartner(createPartnerDto)).rejects.toThrow();
})
});

describe('updatePartner', () => {
it('when supplied with correct data should return new partner', async () => {
jest.spyOn(mockPartnerRepository, 'createQueryBuilder').mockImplementationOnce(
createQueryBuilderMock({
execute: jest
.fn()
.mockResolvedValue({raw: [{ ...mockPartnerEntity, active: false }]})
}) as never, // TODO resolve this typescript issue
);

const response = await service.updatePartner(mockPartnerEntity.id, { active: false });
expect(response).toMatchObject({ ...mockPartnerEntity, active: false });
})

it('when supplied with incorrect partnerName should throw', async () => {
jest.spyOn(mockPartnerRepository, 'createQueryBuilder').mockImplementationOnce(() => {
throw new Error('Error unable to update');
})

await expect(
service.updatePartner(mockPartnerEntity.id, { active: false })
).rejects.toThrow('Error unable to update');
})
})
});
16 changes: 16 additions & 0 deletions src/partner/partner.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { In, Repository } from 'typeorm';
import { PartnerEntity } from '../entities/partner.entity';
import { CreatePartnerDto } from './dtos/create-partner.dto';
import { DeletePartnerDto } from './dtos/delete-partner.dto';
import { UpdatePartnerDto } from './dtos/update-partner.dto';

@Injectable()
export class PartnerService {
Expand Down Expand Up @@ -90,4 +91,19 @@ export class PartnerService {

return 'Successful';
}

async updatePartner(partnerId: string, { active }: UpdatePartnerDto){
const updatedPartnerResponse = await this.partnerRepository
.createQueryBuilder()
.update(PartnerEntity)
.set({ isActive: active })
.where('id = :id', { id: partnerId })
.returning('*')
.execute();
if (updatedPartnerResponse.raw.length > 0) {
return updatedPartnerResponse.raw[0];
} else {
throw new Error('Failed to update partner');
}
}
}
24 changes: 24 additions & 0 deletions src/session-feedback/dtos/session-feedback.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { ApiProperty } from '@nestjs/swagger';
import { IsEnum, IsNotEmpty, IsString } from 'class-validator';
import { FEEDBACK_TAGS_ENUM } from 'src/utils/constants';

export class SessionFeedbackDto {
@IsNotEmpty()
@IsString()
@ApiProperty({ type: String })
sessionId: string;

@IsNotEmpty()
@IsEnum(FEEDBACK_TAGS_ENUM)
@ApiProperty({
enum: FEEDBACK_TAGS_ENUM,
type: String,
example: Object.values(FEEDBACK_TAGS_ENUM),
})
feedbackTags: FEEDBACK_TAGS_ENUM;

@IsNotEmpty()
@IsString()
@ApiProperty({ type: String })
feedbackDescription: string;
}
25 changes: 25 additions & 0 deletions src/session-feedback/session-feedback.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { Body, Controller, Post, UseGuards } from '@nestjs/common';
import { ApiBearerAuth, ApiOperation, ApiTags } from '@nestjs/swagger';
import { ControllerDecorator } from 'src/utils/controller.decorator';
import { FirebaseAuthGuard } from '../firebase/firebase-auth.guard';
import { SessionFeedbackDto } from './dtos/session-feedback.dto';
import { SessionFeedbackService } from './session-feedback.service';

@ApiTags('Session Feedback')
@ControllerDecorator()
@Controller('/v1/session-feedback')
export class SessionFeedbackController {
constructor(private readonly sessionFeedbackService: SessionFeedbackService) {}

@Post()
@ApiBearerAuth('access-token')
@ApiOperation({
description: 'Stores feedback from a user',
})
@UseGuards(FirebaseAuthGuard)
async storeUserFeedback(
@Body() sessionFeedbackDto: SessionFeedbackDto,
): Promise<SessionFeedbackDto> {
return await this.sessionFeedbackService.createSessionFeedback(sessionFeedbackDto);
}
}
48 changes: 48 additions & 0 deletions src/session-feedback/session-feedback.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { SlackMessageClient } from 'src/api/slack/slack-api';
import { ZapierWebhookClient } from 'src/api/zapier/zapier-webhook-client';
import { PartnerAccessEntity } from 'src/entities/partner-access.entity';
import { PartnerEntity } from 'src/entities/partner.entity';
import { SessionFeedbackEntity } from 'src/entities/session-feedback.entity';
import { SessionEntity } from 'src/entities/session.entity';
import { SubscriptionUserEntity } from 'src/entities/subscription-user.entity';
import { SubscriptionEntity } from 'src/entities/subscription.entity';
import { TherapySessionEntity } from 'src/entities/therapy-session.entity';
import { UserEntity } from 'src/entities/user.entity';
import { PartnerAccessService } from 'src/partner-access/partner-access.service';
import { SessionService } from 'src/session/session.service';
import { SubscriptionUserService } from 'src/subscription-user/subscription-user.service';
import { SubscriptionService } from 'src/subscription/subscription.service';
import { TherapySessionService } from 'src/therapy-session/therapy-session.service';
import { UserService } from 'src/user/user.service';
import { SessionFeedbackController } from './session-feedback.controller';
import { SessionFeedbackService } from './session-feedback.service';

@Module({
imports: [
TypeOrmModule.forFeature([
SessionFeedbackEntity,
SessionEntity,
UserEntity,
PartnerAccessEntity,
PartnerEntity,
SubscriptionUserEntity,
SubscriptionEntity,
TherapySessionEntity,
]),
],
controllers: [SessionFeedbackController],
providers: [
SessionFeedbackService,
SessionService,
UserService,
SubscriptionUserService,
SubscriptionService,
PartnerAccessService,
TherapySessionService,
ZapierWebhookClient,
SlackMessageClient,
],
})
export class SessionFeedbackModule {}
Loading

0 comments on commit b82ebff

Please sign in to comment.