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

Merge Develop onto Main #586

Open
wants to merge 21 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
1528fbc
Replaced user with user-entity in session-user (#567)
leoseg Aug 14, 2024
3518f7f
fix: remove console.log
eleanorreem Aug 14, 2024
8c5185d
chore: fix delete all cypress users (#599)
annarhughes Aug 19, 2024
718f7bd
feat: bulk delete users (#600)
annarhughes Aug 19, 2024
8ed2260
Ticket-547 updated sub-user service and controller to use user entity…
ckirby19 Aug 20, 2024
e273b43
fix: rm bulk delete (#601)
annarhughes Aug 21, 2024
221cdb9
chore(deps-dev): bump eslint from 9.3.0 to 9.9.1
dependabot[bot] Aug 26, 2024
61e8599
chore(deps-dev): bump @nestjs/testing from 10.3.10 to 10.4.1
dependabot[bot] Aug 19, 2024
68ce532
chore(deps): bump @nestjs/common from 10.3.10 to 10.4.1
dependabot[bot] Aug 19, 2024
e154cba
chore(deps): bump storyblok-js-client from 6.7.3 to 6.8.4
dependabot[bot] Aug 26, 2024
e591a9f
chore(deps): bump micromatch from 4.0.5 to 4.0.8
dependabot[bot] Aug 26, 2024
382c840
chore(deps): bump storyblok-js-client from 6.8.4 to 6.9.0
dependabot[bot] Aug 26, 2024
ca7a904
545-Replace req user with req userEntity in the user.controller.ts (#…
SangilYun Sep 10, 2024
8ef5d9e
#429: Implement GET /subscription_user Endpoint (#580)
Tim-Quattrochi Sep 10, 2024
932278c
chore(deps): bump newrelic/deployment-marker-action from 2.5.0 to 2.5.1
dependabot[bot] Sep 9, 2024
0c279c9
chore(deps-dev): bump typescript from 5.4.5 to 5.5.4
dependabot[bot] Sep 2, 2024
affaa57
chore(deps-dev): bump ts-jest from 29.2.4 to 29.2.5
dependabot[bot] Sep 2, 2024
840a255
chore(deps): bump storyblok-js-client from 6.9.0 to 6.9.2
dependabot[bot] Sep 9, 2024
15876c0
chore(deps-dev): bump @types/node from 20.12.12 to 22.5.4
dependabot[bot] Sep 9, 2024
8f9ec6f
chore(deps-dev): bump typescript-eslint from 7.17.0 to 8.5.0
dependabot[bot] Sep 10, 2024
027ffcd
Docs: Update OSS community directions and env vars (#644)
kyleecodes Sep 17, 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
21 changes: 11 additions & 10 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
@@ -1,21 +1,22 @@
### Issue link / number:
### Resolves #enter-issue-number

### What changes did you make?
### What changes did you make and why did you make them?

### Why did you make the changes?
### Did you run tests? Share screenshot of results:

### Did you run tests?
### How did you find us? (GitHub, Google search, social media, etc.):

<!---IMPORTANT NOTE ABOUT RUNNING TESTS: ->
<!---ABOUT RUNNING TESTS :->
- Directions for running tests are in the README.md.
- Tests are not required to pass.
- Run unit tests
- Run integration tests if required for contribution.
- Some tests may require multiple runs before success.
- Some test failures may not be due to your contribution and can be ignored.
- If tests fail without resolution, please let us know here.
- Directions for running tests are in the README.md
- Some test failures may not be due to your contribution and can be ignored. We are always upgrading testing performance.

<!--- PR CHECKLIST: PLEASE REMOVE BEFORE SUBMITTING —>
<!--- PR CHECKLIST: —>
Before submitting, check that you have completed the following tasks:
- [ ] Answered the questions above.
- [ ] Read Chayn's Contributing Guidelines in the CONTRIBUTING.md file.
- [ ] Enabled "Allow edits and access to secrets by maintainers" on this PR.
- [ ] If applicable, include images in the description.
After submitting, please be available for discussion. Thank you!
2 changes: 1 addition & 1 deletion .github/workflows/newrelic-release-tracking.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:
run: echo "COMMIT_REF=${{ github.ref_name }}" >> $GITHUB_ENV
# This step creates a new Change Tracking Marker
- name: New Relic Application Deployment Marker
uses: newrelic/[email protected].0
uses: newrelic/[email protected].1
with:
apiKey: ${{ secrets.NEW_RELIC_API_KEY }}
region: 'EU'
Expand Down
125 changes: 43 additions & 82 deletions CONTRIBUTING.md

Large diffs are not rendered by default.

231 changes: 117 additions & 114 deletions README.md

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ services:
- .:/app

db:
image: postgres:14-alpine
image: postgres:16
container_name: bloom-local-db
restart: unless-stopped
ports:
Expand Down
16 changes: 8 additions & 8 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
"dependencies": {
"@mailchimp/mailchimp_marketing": "^3.0.80",
"@nestjs/axios": "^3.0.2",
"@nestjs/common": "^10.3.10",
"@nestjs/common": "^10.4.1",
"@nestjs/config": "^3.2.3",
"@nestjs/core": "^10.3.6",
"@nestjs/platform-express": "^10.4.1",
Expand All @@ -53,33 +53,33 @@
"rimraf": "^6.0.1",
"rollbar": "^2.26.4",
"rxjs": "^7.8.1",
"storyblok-js-client": "^6.7.1",
"storyblok-js-client": "^6.9.2",
"typeorm": "^0.3.20"
},
"devDependencies": {
"@eslint/js": "^9.8.0",
"@golevelup/ts-jest": "^0.5.0",
"@nestjs/cli": "^10.3.2",
"@nestjs/schematics": "^10.1.3",
"@nestjs/testing": "^10.3.10",
"@nestjs/testing": "^10.4.1",
"@types/date-fns": "^2.6.0",
"@types/express": "^4.17.21",
"@types/jest": "^29.5.12",
"@types/lodash": "^4.17.7",
"@types/node": "^20.12.5",
"@types/node": "^22.5.4",
"@types/supertest": "^6.0.2",
"eslint": "^9.0.0",
"eslint": "^9.9.1",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-prettier": "^5.1.3",
"jest": "^29.7.0",
"prettier": "^3.3.3",
"supertest": "^7.0.0",
"ts-jest": "^29.2.4",
"ts-jest": "^29.2.5",
"ts-loader": "^9.5.1",
"ts-node": "^10.9.2",
"tsconfig-paths": "^4.2.0",
"typescript": "^5.4.5",
"typescript-eslint": "^7.17.0"
"typescript": "^5.5.4",
"typescript-eslint": "^8.5.0"
},
"engines": {
"node": "20.x",
Expand Down
5 changes: 1 addition & 4 deletions src/api/crisp/crisp-api.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { AxiosResponse } from 'axios';
import { Logger } from '../../logger/logger';
import { crispToken, crispWebsiteId } from '../../utils/constants';
import apiCall from '../apiCalls';
import {
Expand All @@ -18,8 +17,6 @@ const headers = {
'-ContentType': 'application/json',
};

const logger = new Logger('UserService');

export const createCrispProfile = async (
newPeopleProfile: CrispProfileBase,
): Promise<AxiosResponse<NewCrispProfileBaseResponse>> => {
Expand Down Expand Up @@ -105,7 +102,7 @@ export const deleteCrispProfile = async (email: string) => {
headers,
});
} catch (error) {
logger.error(`Delete crisp profile API call failed: ${error}`);
throw new Error(`Delete crisp profile API call failed: ${error}`);
}
};

Expand Down
5 changes: 4 additions & 1 deletion src/api/mailchimp/mailchimp-api.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
import mailchimp from '@mailchimp/mailchimp_marketing';
import { createHash } from 'crypto';
import { mailchimpApiKey, mailchimpAudienceId, mailchimpServerPrefix } from 'src/utils/constants';
import { Logger } from '../../logger/logger';
import {
ListMember,
ListMemberPartial,
MAILCHIMP_MERGE_FIELD_TYPES,
UpdateListMemberRequest,
} from './mailchimp-api.interfaces';

const logger = new Logger('MailchimpAPI');

mailchimp.setConfig({
apiKey: mailchimpApiKey,
server: mailchimpServerPrefix,
Expand Down Expand Up @@ -107,7 +110,7 @@ export const deleteMailchimpProfile = async (email: string) => {
try {
return await mailchimp.lists.deleteListMember(mailchimpAudienceId, getEmailMD5Hash(email));
} catch (error) {
throw new Error(`Delete mailchimp profile API call failed: ${error}`);
logger.warn(`Delete mailchimp profile API call failed: ${error}`);
}
};

Expand Down
2 changes: 1 addition & 1 deletion src/partner-admin/super-admin-auth.guard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ export class SuperAdminAuthGuard implements CanActivate {
);
}
try {
const user = await this.userRepository.findOneBy({ firebaseUid: userUid });
const user = await this.userRepository.findOneByOrFail({ firebaseUid: userUid });
request['userEntity'] = user;
return !!user.isSuperAdmin && user.email.indexOf('@chayn.co') !== -1;
} catch (error) {
Expand Down
8 changes: 4 additions & 4 deletions src/session-user/session-user.controller.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Body, Controller, Post, Req, UseGuards } from '@nestjs/common';
import { ApiBearerAuth, ApiOperation, ApiTags } from '@nestjs/swagger';
import { Request } from 'express';
import { GetUserDto } from 'src/user/dtos/get-user.dto';
import {UserEntity} from '../entities/user.entity';
import { ControllerDecorator } from 'src/utils/controller.decorator';
import { FirebaseAuthGuard } from '../firebase/firebase-auth.guard';
import { UpdateSessionUserDto } from './dtos/update-session-user.dto';
Expand All @@ -22,7 +22,7 @@ export class SessionUserController {
@UseGuards(FirebaseAuthGuard)
async createSessionUser(@Req() req: Request, @Body() createSessionUserDto: UpdateSessionUserDto) {
return await this.sessionUserService.createSessionUser(
req['user'] as GetUserDto,
req['userEntity'] as UserEntity,
createSessionUserDto,
);
}
Expand All @@ -35,7 +35,7 @@ export class SessionUserController {
@UseGuards(FirebaseAuthGuard)
async complete(@Req() req: Request, @Body() updateSessionUserDto: UpdateSessionUserDto) {
return await this.sessionUserService.setSessionUserCompleted(
req['user'] as GetUserDto,
req['userEntity'] as UserEntity,
updateSessionUserDto,
true,
);
Expand All @@ -50,7 +50,7 @@ export class SessionUserController {
@UseGuards(FirebaseAuthGuard)
async incomplete(@Req() req: Request, @Body() updateSessionUserDto: UpdateSessionUserDto) {
return await this.sessionUserService.setSessionUserCompleted(
req['user'] as GetUserDto,
req['userEntity'] as UserEntity,
updateSessionUserDto,
false,
);
Expand Down
5 changes: 2 additions & 3 deletions src/session-user/session-user.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import { CourseEntity } from '../entities/course.entity';
import { SessionUserEntity } from '../entities/session-user.entity';
import { Logger } from '../logger/logger';
import { SessionService } from '../session/session.service';
import { GetUserDto } from '../user/dtos/get-user.dto';
import { STORYBLOK_STORY_STATUS_ENUM } from '../utils/constants';
import { formatCourseUserObject, formatCourseUserObjects } from '../utils/serialize';
import { SessionUserDto } from './dtos/session-user.dto';
Expand Down Expand Up @@ -86,7 +85,7 @@ export class SessionUserService {
});
}

public async createSessionUser({ user }: GetUserDto, { storyblokId }: UpdateSessionUserDto) {
public async createSessionUser(user: UserEntity, { storyblokId }: UpdateSessionUserDto) {
const session = await this.sessionService.getSessionByStoryblokId(storyblokId);

if (!session) {
Expand Down Expand Up @@ -132,7 +131,7 @@ export class SessionUserService {
}

public async setSessionUserCompleted(
{ user }: GetUserDto,
user : UserEntity,
{ storyblokId }: UpdateSessionUserDto,
completed: boolean,
) {
Expand Down
39 changes: 39 additions & 0 deletions src/subscription-user/dto/get-subscription-user.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { ApiProperty } from '@nestjs/swagger';
import { Expose } from 'class-transformer';
import { IsDefined, IsNotEmpty, IsOptional, IsString } from 'class-validator';

@Expose()
export class GetSubscriptionUserDto {
@ApiProperty()
@IsString()
@IsDefined()
@IsNotEmpty()
id: string;

@ApiProperty()
@IsString()
@IsDefined()
@IsNotEmpty()
subscriptionId: string;

@ApiProperty()
@IsString()
@IsDefined()
@IsNotEmpty()
@Expose()
subscriptionName: string;

@ApiProperty()
@IsString()
@IsDefined()
@IsNotEmpty()
subscriptionInfo: string;

@ApiProperty()
@IsDefined()
createdAt: Date;

@ApiProperty()
@IsOptional()
cancelledAt: Date | null;
}
28 changes: 24 additions & 4 deletions src/subscription-user/subscription-user.controller.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import { Body, Controller, Param, Patch, Post, Req, UseGuards } from '@nestjs/common';
import { Body, Controller, Get, Param, Patch, Post, Req, UseGuards } from '@nestjs/common';
import { ApiBearerAuth, ApiOperation, ApiParam, ApiTags } from '@nestjs/swagger';
import { Request } from 'express';
import { UserEntity } from 'src/entities/user.entity';
import { mapToSubscriptionUserDtos } from 'src/utils/serialize';
import { FirebaseAuthGuard } from '../firebase/firebase-auth.guard';
import { ControllerDecorator } from '../utils/controller.decorator';
import { CreateSubscriptionUserDto } from './dto/create-subscription-user.dto';
import { GetSubscriptionUserDto } from './dto/get-subscription-user.dto';
import { UpdateSubscriptionUserDto } from './dto/update-subscription-user.dto';
import { ISubscriptionUser } from './subscription-user.interface';
import { SubscriptionUserService } from './subscription-user.service';
Expand All @@ -14,6 +17,23 @@ import { SubscriptionUserService } from './subscription-user.service';
export class SubscriptionUserController {
constructor(private readonly subscriptionUserService: SubscriptionUserService) {}

@Get('/')
@ApiBearerAuth('access-token')
@ApiOperation({
description: 'Returns all the subscriptions of the authenticated user.',
})
@UseGuards(FirebaseAuthGuard)
async getUserSubscriptions(@Req() req: Request): Promise<GetSubscriptionUserDto[]> {
const user = req['userEntity'] as UserEntity;
const userId = user.id;

const userSubscriptions = await this.subscriptionUserService.getSubscriptions(userId);

const subscriptionDtos = mapToSubscriptionUserDtos(userSubscriptions);

return subscriptionDtos;
}

@Post('/whatsapp')
@ApiBearerAuth('access-token')
@ApiOperation({
Expand All @@ -26,7 +46,7 @@ export class SubscriptionUserController {
@Body() createSubscriptionUserDto: CreateSubscriptionUserDto,
): Promise<ISubscriptionUser | undefined> {
return await this.subscriptionUserService.createWhatsappSubscription(
req['user'],
req['userEntity'] as UserEntity,
createSubscriptionUserDto,
);
}
Expand All @@ -44,8 +64,8 @@ export class SubscriptionUserController {
@Body() updateSubscriptionsDto: UpdateSubscriptionUserDto,
): Promise<ISubscriptionUser | undefined> {
return this.subscriptionUserService.cancelWhatsappSubscription(
req['user'].user.id,
req['user'].user.email,
req['userEntity'].id,
req['userEntity'].email,
updateSubscriptionsDto,
id,
);
Expand Down
20 changes: 20 additions & 0 deletions src/subscription-user/subscription-user.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,4 +61,24 @@ describe('SubscriptionUserService', () => {
]);
});
});

describe('getUserSubscriptions', () => {
it('should return a list of subscriptions for the given userId', async () => {
const result = await service.getSubscriptions(mockUserEntity.id);

expect(result).toMatchObject([mockSubscriptionUserEntity]);
});

it('should return an empty array if the user has no subscriptions', async () => {
mockedSubscriptionUserRepository.find.mockResolvedValueOnce([]);

const result = await service.getSubscriptions(mockUserEntity.id);

expect(result).toEqual([]);
expect(mockedSubscriptionUserRepository.find).toHaveBeenCalledWith({
where: { userId: mockUserEntity.id },
relations: ['subscription'],
});
});
});
});
19 changes: 17 additions & 2 deletions src/subscription-user/subscription-user.service.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { SubscriptionUserEntity } from 'src/entities/subscription-user.entity';
import { UserEntity } from 'src/entities/user.entity';
import { IsNull, Repository } from 'typeorm';
import { ZapierWebhookClient } from '../api/zapier/zapier-webhook-client';
import { Logger } from '../logger/logger';
import { SubscriptionService } from '../subscription/subscription.service';
import { GetUserDto } from '../user/dtos/get-user.dto';
import { WhatsappSubscriptionStatusEnum } from '../utils/constants';
import { formatSubscriptionObject } from '../utils/serialize';
import { CreateSubscriptionUserDto } from './dto/create-subscription-user.dto';
Expand All @@ -24,7 +24,7 @@ export class SubscriptionUserService {
) {}

async createWhatsappSubscription(
{ user }: GetUserDto,
user: UserEntity,
createSubscriptionUserDto: CreateSubscriptionUserDto,
): Promise<ISubscriptionUser | undefined> {
const whatsapp = await this.subscriptionService.getSubscription('whatsapp');
Expand Down Expand Up @@ -153,4 +153,19 @@ export class SubscriptionUserService {
);
}
}

async getSubscriptions(userId: string): Promise<SubscriptionUserEntity[]> {
try {
const userSubscriptions = await this.subscriptionUserRepository.find({
where: { userId: userId },
relations: ['subscription'],
});

this.logger.log(`User ${userId} has ${userSubscriptions.length} subscriptions`);

return userSubscriptions;
} catch (err) {
throw new HttpException(`getSubscriptions error - ${err}`, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
}
Loading
Loading