Skip to content

Commit

Permalink
Merge pull request #358 from chaynHQ/develop
Browse files Browse the repository at this point in the history
Merge Develop onto Main
  • Loading branch information
annarhughes authored Jan 5, 2024
2 parents b479f4e + 7c0efcd commit 64ddea9
Show file tree
Hide file tree
Showing 12 changed files with 378 additions and 147 deletions.
11 changes: 7 additions & 4 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
### Issue ticket link / number:
### Issue link / number:

### What changes did you make?

### Why did you make the changes?

<!--- PR CHECKLIST: PLEASE REMOVE BEFORE SUBMITTING —>
- [ ] You have answered the above questions.
- [ ] You have followed the guidelines in the CONTRIBUTING.md file
- [ ] You have a descriptive and concise PR title.
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!
1 change: 1 addition & 0 deletions .github/workflows/.ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ jobs:
runs-on: ubuntu-latest
env:
SIMPLYBOOK_CREDENTIALS: '{"login": "login"}'
STORYBLOK_WEBHOOK_SECRET: ${{ secrets.STORYBLOK_WEBHOOK_SECRET }}
steps:
- uses: actions/checkout@v2
- name: Get yarn cache directory path
Expand Down
23 changes: 23 additions & 0 deletions .github/workflows/new-assignee-comment.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# This workflow comments helpful info in issues when they are assigned.
# For more information, see:
# https://github.com/actions/first-interaction

name: New Assignee Issue Comment
on:
issues:
types:
- assigned
jobs:
add-comment:
runs-on: ubuntu-latest
permissions:
issues: write
steps:
- name: Add comment
run: gh issue comment "$NUMBER" --body "$BODY"
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GH_REPO: ${{ github.repository }}
NUMBER: ${{ github.event.issue.number }}
BODY: 'Thank you for your interest in contributing to Chayn! Please carefully read the CONTRIBUTING.md file and the README.md file for guidance. Let us know if you have any questions. Good luck!'

35 changes: 35 additions & 0 deletions .github/workflows/stale-issue-management.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# This workflow warns when issues have had no activity for a specified amount of time.
# For more information, see:
# https://github.com/actions/stale
name: Mark Stale Issues

on:
# Enable manual run from the Actions tab.
workflow_dispatch:
# Scheduled to run at 12:00 on every 1st of the month.
schedule:
- cron: '0 12 1 * *'

jobs:
stale:

runs-on: ubuntu-latest
permissions:
# PR permissions can be added here
issues: write

steps:
- uses: actions/stale@v5
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
remove-stale-when-updated: false
include-only-assigned: true
# Disable closing issues
days-before-close: -1
stale-issue-label: 'stale'
# Ignores comments as activity, only looks at date of issue creation.
ignore-updates: true
# Ignore developer staff and frequent contributors
exempt-assignees: 'kyleecodes, swetha-charles, eleanorreem, annarhughes, tarebyte'
stale-issue-message: 'As per Chayn policy, after 60 days of inactivity, we will be unassigning this issue to open it back up for contributors. Please comment to be re-assigned. Thank you for your interest in contributing to Chayn!'

8 changes: 3 additions & 5 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
{
"eslint.packageManager": "yarn",
"eslint.alwaysShowStatus": true,
"editor.formatOnPaste": true,
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.organizeImports": true,
"source.fixAll": true,
"source.fixAll.eslint": true
"source.organizeImports": "explicit",
"source.fixAll": "explicit",
"source.fixAll.eslint": "explicit"
},
"editor.defaultFormatter": "esbenp.prettier-vscode",
"vetur.format.defaultFormatterOptions": {
Expand Down
252 changes: 171 additions & 81 deletions CONTRIBUTING.md

Large diffs are not rendered by default.

66 changes: 41 additions & 25 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,35 +1,38 @@
# Bloom
# Welcome to Bloom

Bloom is a remote trauma support service from Chayn, a global charity supporting survivors of abuse across borders. Bloom is our flagship product; a free, web-based support service designed for anyone who has experienced or is currently experiencing domestic or sexual abuse. Through a combination of online video-based courses, anonymous interaction and 1:1 chat, Bloom aims to provide tailored information, guidance, everyday tools, and comforting words to cope with traumatic events. 💖

## Get involved

If you would like to help Chayn and receive special access to our organization and volunteer opportunities, please [visit our Getting Involved guide](https://chayn.notion.site/Get-involved-423c067536f3426a88005de68f0cab19). We'll get back to you to schedule an onboarding call. Other ways to get involved and support us are [donating](https://www.paypal.me/chaynhq), starring this repo and making an open-source contribution here on GitHub, and supporting us on social media!
[![Bloom Backend CI Pipeline](https://github.com/chaynHQ/bloom-backend/actions/workflows/.ci.yml/badge.svg)](https://github.com/chaynHQ/bloom-backend/actions/workflows/.ci.yml)

Our social medias:
**Currently in active development.**

Website - [Chayn](https://www.chayn.co/)
Bloom is a remote trauma support service from Chayn, a global charity supporting survivors of abuse across borders. Bloom is our flagship product; a free, web-based support service designed for anyone who has experienced or is currently experiencing domestic or sexual abuse. Through a combination of online video-based courses, anonymous interaction and 1:1 chat, Bloom aims to provide tailored information, guidance, everyday tools, and comforting words to cope with traumatic events. 💖

Twitter - [@ChaynHQ](https://twitter.com/ChaynHQ)
## Get Involved

Instagram - [@chaynhq](https://www.instagram.com/chaynhq/)
Do you want to make an impact with Chayn and receive special access to our organization and volunteer opportunities? Please visit our [Getting Involved Guide](https://chayn.notion.site/Get-involved-423c067536f3426a88005de68f0cab19) to get started!

Youtube - [Chayn Team](https://www.youtube.com/channel/UC5_1Ci2SWVjmbeH8_USm-Bg)
Other ways you can support Chayn are [donating](https://www.paypal.me/chaynhq), starring this repository ⭐ (so we can find more contributors like you!), making an open-source contribution, and supporting us on social media!

LinkedIn - [@chayn](https://www.linkedin.com/company/chayn)
Find us online:

# Bloom Backend
- Website: [https://www.chayn.co/](https://www.chayn.co/)
- Linktree: [https://linktr.ee/chayn](https://linktr.ee/chayn)
- Twitter: [@ChaynHQ](https://twitter.com/ChaynHQ)
- Instagram: [@chaynhq](https://www.instagram.com/chaynhq/)
- Youtube: [Chayn Team](www.youtube.com/@chaynhq)
- LinkedIn: [@chayn](https://www.linkedin.com/company/chayn)

[![Bloom Backend CI Pipeline](https://github.com/chaynHQ/bloom-backend/actions/workflows/.ci.yml/badge.svg)](https://github.com/chaynHQ/bloom-backend/actions/workflows/.ci.yml)
# Contributing to Bloom Backend

**Currently in active development**
Before making a contribution, please read our Contributing Guidelines in the [CONTRIBUTING.md](/CONTRIBUTING.md) file.

## Contribution
We ask all contributors to follow our [Contributing Guidelines](/CONTRIBUTING.md) to help Chayn developers maintain open-source best practices.

To make a contribution, please read our Contributing Guidelines in [CONTRIBUTING.md](https://github.com/chaynHQ/bloom-backend/blob/develop/CONTRIBUTING.md)
Happy coding! ⭐

## Technologies Used:

For a more detailed explanation of this project's key concepts and architecture, please visit the [/docs directory](https://github.com/chaynHQ/bloom-backend/tree/develop/docs).

- [NestJS](https://nestjs.com/) - NodeJs framework for building scalable and reliable server-side applications
- [PostgreSQL](https://www.postgresql.org/about/) - Object-relational SQL database system
- [TypeORM](https://github.com/typeorm/typeorm) - Object Relational Mapper library
Expand Down Expand Up @@ -58,11 +61,23 @@ To make a contribution, please read our Contributing Guidelines in [CONTRIBUTING
yarn
```

### Create `.env` file
### Configure Environment Variables

- If you are an official Chayn volunteer: please get in touch with the team for access to our environment variables.

- If you are an open-source contributor: create a new `.env` file and populate it with the following variables.

**The Firebase tokens are required for running locally, the Simplybook tokens are required for testing, and all other variables are optional.**

Include the following environment variables in a `.env` file.
You will need to gather your own Firebase tokens. Here are resources to get started:

You will need to gather the following tokens from [Firebase](https://firebase.google.com/docs/projects/api-keys), these are required for the app to function. However, the [Simplybook](https://simplybook.me/en/) variables are required to pass all tests. But since these tests use mocked data, **you do not need to use real Simplybook variables, simply copy paste the values given below.** If you require real Simplybook environment variables, use the same format given below. All other environment variables from Rollbar, Zapier, Slack, Crisp, and Mailchimp are optional. If you're a volunteer loading up the back-end, please get in touch with the team for access to the environment variables.
- [Firebase Docs: Auth](https://firebase.google.com/docs/auth)
- [Firebase Docs: API Keys](https://firebase.google.com/docs/projects/api-keys)
- [Firebase Docs: Projects](https://firebase.google.com/docs/projects/learn-more#project-identifiers)
- [Firebase Docs: Get Started with Fundamentals](https://firebase.google.com/docs/guides)

The Simplybook variables can be mocked data, meaning **you do not need to use real Simplybook variables, simply copy paste the values given below.**
If you acquire real Simplybook environment variables, use the same format given below.

```
ROLLBAR_ENV=development
Expand Down Expand Up @@ -96,8 +111,8 @@ SIMPLYBOOK_CREDENTIALS='{"login":"testlogin","password":"testpassword","company"
SIMPLYBOOK_COMPANY_NAME=testcompany
# VARIABLES BELOW ARE ALL OPTIONAL
ROLLBAR_TOKEN=
ZAPIER_TOKEN=
ROLLBAR_TOKEN=
ZAPIER_TOKEN=
SLACK_WEBHOOK_URL=
CRISP_TOKEN=
CRISP_WEBSITE_TOKEN=
Expand Down Expand Up @@ -214,15 +229,16 @@ After the above has been confirmed, run
```bash
bash seed-local-db.sh
```
For a more detailed explanation of this project's key concepts and architecture, please visit the [/docs directory](https://github.com/chaynHQ/bloom-backend/tree/develop/docs).

## Git flow and deployment
## Git Flow and Deployment

**The develop branch is our source of truth, not main.**

Create new branches from the `develop` base branch. There is no need to run the build command before pushing changes to GitHub, simply push and create a pull request for the new branch. GitHub Actions will run build and linting tasks automatically. Rebase and merge feature/bug branches into `develop`.

This will trigger an automatic deployment to the staging app by Heroku.

When changes have been tested in staging, merge `staging` into `main`. This will trigger an automatic deployment to the production app by Heroku.
When changes have been tested in staging, merge `develop` into `main`. This will trigger an automatic deployment to the production app by Heroku.

## Swagger

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
"@mailchimp/mailchimp_transactional": "^1.0.47",
"@nestjs/common": "^8.0.0",
"@nestjs/config": "^1.0.2",
"@nestjs/core": "^8.0.0",
"@nestjs/core": "^9.0.5",
"@nestjs/passport": "^8.0.1",
"@nestjs/platform-express": "^8.0.0",
"@nestjs/swagger": "^5.1.4",
Expand Down
7 changes: 4 additions & 3 deletions src/webhooks/webhooks.controller.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Body, Controller, Logger, Post, UseGuards } from '@nestjs/common';
import { Body, Controller, Headers, Logger, Post, UseGuards } from '@nestjs/common';
import { ApiBody, ApiTags } from '@nestjs/swagger';
import { EventLogEntity } from 'src/entities/event-log.entity';
import { TherapySessionEntity } from 'src/entities/therapy-session.entity';
Expand Down Expand Up @@ -53,7 +53,8 @@ export class WebhooksController {

@Post('storyblok')
@ApiBody({ type: StoryDto })
async updateStory(@Body() storyDto: StoryDto) {
return this.webhooksService.updateStory(storyDto);
async updateStory(@Body() data: StoryDto, @Headers() headers) {
const signature: string | undefined = headers['webhook-signature'];
return this.webhooksService.updateStory(data, signature);
}
}
70 changes: 51 additions & 19 deletions src/webhooks/webhooks.service.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { createMock } from '@golevelup/ts-jest';
import { Test, TestingModule } from '@nestjs/testing';
import { createHmac } from 'crypto';
import { format, sub } from 'date-fns';
import startOfDay from 'date-fns/startOfDay';
import { MailchimpClient } from 'src/api/mailchimp/mailchip-api';
Expand Down Expand Up @@ -48,6 +49,12 @@ import { EmailCampaignRepository } from './email-campaign/email-campaign.reposit
import { TherapySessionRepository } from './therapy-session.repository';
import { WebhooksService } from './webhooks.service';

const webhookSecret = process.env.STORYBLOK_WEBHOOK_SECRET;

const getWebhookSignature = (body) => {
return createHmac('sha1', webhookSecret).update(JSON.stringify(body)).digest('hex');
};

// Difficult to mock classes as well as node modules.
// This seemed the best approach
jest.mock('storyblok-js-client', () => {
Expand All @@ -57,6 +64,7 @@ jest.mock('storyblok-js-client', () => {
};
});
});

jest.mock('src/api/simplybook/simplybook-api', () => {
return {
getBookingsForDate: async () => [
Expand Down Expand Up @@ -181,30 +189,44 @@ describe('WebhooksService', () => {
});
expect.assertions(1);

return expect(
service.updateStory({
action: STORYBLOK_STORY_STATUS_ENUM.DELETED,
story_id: mockSession.storyblokId,
text: '',
}),
).rejects.toThrowError('STORYBLOK STORY NOT FOUND');
const body = {
action: STORYBLOK_STORY_STATUS_ENUM.DELETED,
story_id: mockSession.storyblokId,
text: '',
};

return expect(service.updateStory(body, getWebhookSignature(body))).rejects.toThrowError(
'STORYBLOK STORY NOT FOUND',
);
});

it('when action is deleted, story should be set as deleted in database', async () => {
const deletedStory = (await service.updateStory({
const body = {
action: STORYBLOK_STORY_STATUS_ENUM.DELETED,
story_id: mockSession.storyblokId,
text: '',
})) as SessionEntity;
};

const deletedStory = (await service.updateStory(
body,
getWebhookSignature(body),
)) as SessionEntity;

expect(deletedStory.status).toBe(STORYBLOK_STORY_STATUS_ENUM.DELETED);
});

it('when action is unpublished, story should be set as unpublished in database', async () => {
const unpublished = (await service.updateStory({
const body = {
action: STORYBLOK_STORY_STATUS_ENUM.UNPUBLISHED,
story_id: mockSession.storyblokId,
text: '',
})) as SessionEntity;
};

const unpublished = (await service.updateStory(
body,
getWebhookSignature(body),
)) as SessionEntity;

expect(unpublished.status).toBe(STORYBLOK_STORY_STATUS_ENUM.UNPUBLISHED);
});

Expand Down Expand Up @@ -245,11 +267,13 @@ describe('WebhooksService', () => {
return course2;
});

const session = (await service.updateStory({
const body = {
action: STORYBLOK_STORY_STATUS_ENUM.PUBLISHED,
story_id: mockCourse.storyblokId,
text: '',
})) as SessionEntity;
};

const session = (await service.updateStory(body, getWebhookSignature(body))) as SessionEntity;

expect(courseFindOneSpy).toBeCalledWith({
storyblokUuid: 'anotherCourseUuId',
Expand Down Expand Up @@ -286,11 +310,13 @@ describe('WebhooksService', () => {

const courseFindOneSpy = jest.spyOn(mockedCourseRepository, 'findOne');

const session = (await service.updateStory({
const body = {
action: STORYBLOK_STORY_STATUS_ENUM.PUBLISHED,
story_id: mockSession.storyblokId,
text: '',
})) as SessionEntity;
};

const session = (await service.updateStory(body, getWebhookSignature(body))) as SessionEntity;

expect(session).toEqual(mockSession);
expect(courseFindOneSpy).toBeCalledWith({
Expand Down Expand Up @@ -342,11 +368,14 @@ describe('WebhooksService', () => {
},
};
});
const session = (await service.updateStory({

const body = {
action: STORYBLOK_STORY_STATUS_ENUM.PUBLISHED,
story_id: mockSession.storyblokId,
text: '',
})) as SessionEntity;
};

const session = (await service.updateStory(body, getWebhookSignature(body))) as SessionEntity;

expect(session).toEqual(mockSession);
expect(sessionSaveRepoSpy).toBeCalledWith({
Expand All @@ -373,11 +402,14 @@ describe('WebhooksService', () => {
get: async () => mockCourseStoryblokResult,
};
});
const course = (await service.updateStory({

const body = {
action: STORYBLOK_STORY_STATUS_ENUM.PUBLISHED,
story_id: 5678,
text: '',
})) as CourseEntity;
};

const course = (await service.updateStory(body, getWebhookSignature(body))) as CourseEntity;

expect(course).toEqual(mockCourse);
expect(courseFindOneRepoSpy).toBeCalledWith({
Expand Down
Loading

0 comments on commit 64ddea9

Please sign in to comment.