Skip to content

Commit

Permalink
Tracking pull request to merge release-2.10.0 to master (#2574)
Browse files Browse the repository at this point in the history
* feat: updated feature branch references

* chore: updated branch number

* chore: updated readme versions

* tfrs-2496tooltip font size increase (#2589)

* TFRS - Remove link to credit market report on dashboard (#2586)

* fix: minor fix and unit tests for user notifications (#2584)

Co-authored-by: Your Name <[email protected]>

* fix for rescind status not saving in history

* adding LCFS box in BCeID dashboard

* adding check if previous trasfer status is already rescinded

* revise messaging for category D checkbox

* fixing typo

---------

Co-authored-by: Your Name <[email protected]>
Co-authored-by: jig-patel <[email protected]>
  • Loading branch information
3 people committed Sep 21, 2023
1 parent be06494 commit 06f401c
Show file tree
Hide file tree
Showing 16 changed files with 283 additions and 55 deletions.
4 changes: 2 additions & 2 deletions .github/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@

## Primary Pipelines

* dev-release.yaml (TFRS Dev release-2.9.0): the pipeline is automatically triggered when there is a commit to the release branch
* tfrs-release.yaml (TFRS release-2.9.0): the pipelin builds the release and deploy on Test and Prod, it needs to be manually triggered
* dev-release.yaml (TFRS Dev release-2.10.0): the pipeline is automatically triggered when there is a commit to the release branch
* tfrs-release.yaml (TFRS release-2.10.0): the pipeline builds the release and deploys on Test and Prod, it needs to be manually triggered
* create-release.yaml (Create Release after merging to master): tag and create the release after merging release branch to master. The description of the tracking pull request becomes release notes

## Other Pipelines
Expand Down
8 changes: 4 additions & 4 deletions .github/workflows/dev-release.yaml
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
## For each release, the value of name, branches, RELEASE_NAME and PR_NUMBER need to be adjusted accordingly
## For each release, update lib/config.js: version and releaseBranch

name: TFRS Dev release-2.9.0
name: TFRS Dev release-2.10.0

on:
push:
branches: [ release-2.9.0 ]
branches: [ release-2.10.0 ]
paths:
- frontend/**
- backend/**
Expand All @@ -15,8 +15,8 @@ on:
env:
## The pull request number of the Tracking pull request to merge the release branch to main
## Also remember to update the version in .pipeline/lib/config.js
PR_NUMBER: 2404
RELEASE_NAME: release-2.9.0
PR_NUMBER: 2574
RELEASE_NAME: release-2.10.0

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/tfrs-release.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
## For each release, the value of name, branches, RELEASE_NAME and PR_NUMBER need to be adjusted accordingly
## For each release, update lib/config.js: version and releaseBranch

name: TFRS release-2.9.0
name: TFRS release-2.10.0

on:
workflow_dispatch:
Expand All @@ -10,8 +10,8 @@ on:
env:
## The pull request number of the Tracking pull request to merge the release branch to main
## Also remember to update the version in .pipeline/lib/config.js
PR_NUMBER: 2404
RELEASE_NAME: release-2.9.0
PR_NUMBER: 2574
RELEASE_NAME: release-2.10.0

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
Expand Down
4 changes: 2 additions & 2 deletions .pipeline/lib/config.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
'use strict';
const options= require('@bcgov/pipeline-cli').Util.parseArguments()
const changeId = options.pr //aka pull-request
const version = '2.9.0'
const version = '2.10.0'
const name = 'tfrs'
const ocpName = 'apps.silver.devops'

Expand All @@ -13,7 +13,7 @@ options.git.repository='tfrs'
const phases = {
build: { namespace:'0ab226-tools' , name: `${name}`, phase: 'build' , changeId:changeId, suffix: `-build-${changeId}` ,
instance: `${name}-build-${changeId}` , version:`${version}-${changeId}`, tag:`build-${version}-${changeId}`,
releaseBranch: 'release-2.9.0'
releaseBranch: 'release-2.10.0'
},
dev: {namespace:'0ab226-dev' , name: `${name}`, phase: 'dev' , changeId:changeId, suffix: `-dev` ,
instance: `${name}-dev` , version:`${version}`, tag:`dev-${version}`, dbServiceName: 'tfrs-spilo',
Expand Down
2 changes: 1 addition & 1 deletion backend/api/notifications/notifications.py
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,7 @@ def update_subscription(
@transaction.atomic
def send_notification(
message: str,
interested_organization: Organization,
interested_organization: Organization = None,
interested_roles: List[Role] = [],
related_credit_trade: CreditTrade = None,
related_document: Document = None,
Expand Down
101 changes: 101 additions & 0 deletions backend/api/tests/payloads/notification_payloads.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
notifications_payload_active = [
{"notificationType":"CREDIT_TRANSFER_CREATED","channel":"IN_APP","subscribed":True},
{"notificationType":"CREDIT_TRANSFER_CREATED","channel":"EMAIL","subscribed":True},
{"notificationType":"CREDIT_TRANSFER_SIGNED_1OF2","channel":"IN_APP","subscribed":True},
{"notificationType":"CREDIT_TRANSFER_SIGNED_1OF2","channel":"EMAIL","subscribed":True},
{"notificationType":"CREDIT_TRANSFER_PROPOSAL_REFUSED","channel":"IN_APP","subscribed":True},
{"notificationType":"CREDIT_TRANSFER_PROPOSAL_REFUSED","channel":"EMAIL","subscribed":True},
{"notificationType":"CREDIT_TRANSFER_SIGNED_2OF2","channel":"IN_APP","subscribed":True},
{"notificationType":"CREDIT_TRANSFER_SIGNED_2OF2","channel":"EMAIL","subscribed":True},
{"notificationType":"CREDIT_TRANSFER_APPROVED","channel":"IN_APP","subscribed":True},
{"notificationType":"CREDIT_TRANSFER_APPROVED","channel":"EMAIL","subscribed":True},
{"notificationType":"CREDIT_TRANSFER_DECLINED","channel":"IN_APP","subscribed":True},
{"notificationType":"CREDIT_TRANSFER_DECLINED","channel":"EMAIL","subscribed":True},
{"notificationType":"CREDIT_TRANSFER_RESCINDED","channel":"IN_APP","subscribed":True},
{"notificationType":"CREDIT_TRANSFER_RESCINDED","channel":"EMAIL","subscribed":True},
{"notificationType":"PVR_APPROVED","channel":"IN_APP","subscribed":True},
{"notificationType":"PVR_APPROVED","channel":"EMAIL","subscribed":True},
{"notificationType":"PVR_DECLINED","channel":"IN_APP","subscribed":True},
{"notificationType":"PVR_DECLINED","channel":"EMAIL","subscribed":True},
{"notificationType":"DOCUMENT_PENDING_SUBMISSION","channel":"IN_APP","subscribed":True},
{"notificationType":"DOCUMENT_PENDING_SUBMISSION","channel":"EMAIL","subscribed":True},
{"notificationType":"DOCUMENT_SUBMITTED","channel":"IN_APP","subscribed":True},
{"notificationType":"DOCUMENT_SUBMITTED","channel":"EMAIL","subscribed":True},
{"notificationType":"DOCUMENT_RECEIVED","channel":"IN_APP","subscribed":True},
{"notificationType":"DOCUMENT_RECEIVED","channel":"EMAIL","subscribed":True},
{"notificationType":"DOCUMENT_ARCHIVED","channel":"IN_APP","subscribed":True},
{"notificationType":"DOCUMENT_ARCHIVED","channel":"EMAIL","subscribed":True},
{"notificationType":"COMPLIANCE_REPORT_DRAFT","channel":"IN_APP","subscribed":True},
{"notificationType":"COMPLIANCE_REPORT_DRAFT","channel":"EMAIL","subscribed":True},
{"notificationType":"COMPLIANCE_REPORT_SUBMITTED","channel":"IN_APP","subscribed":True},
{"notificationType":"COMPLIANCE_REPORT_SUBMITTED","channel":"EMAIL","subscribed":True},
{"notificationType":"COMPLIANCE_REPORT_REQUESTED_SUPPLEMENTAL","channel":"IN_APP","subscribed":True},
{"notificationType":"COMPLIANCE_REPORT_REQUESTED_SUPPLEMENTAL","channel":"EMAIL","subscribed":True},
{"notificationType":"COMPLIANCE_REPORT_ACCEPTED","channel":"IN_APP","subscribed":True},
{"notificationType":"COMPLIANCE_REPORT_ACCEPTED","channel":"EMAIL","subscribed":True},
{"notificationType":"COMPLIANCE_REPORT_REJECTED","channel":"IN_APP","subscribed":True},
{"notificationType":"COMPLIANCE_REPORT_REJECTED","channel":"EMAIL","subscribed":True},
{"notificationType":"EXCLUSION_REPORT_DRAFT","channel":"IN_APP","subscribed":True},
{"notificationType":"EXCLUSION_REPORT_DRAFT","channel":"EMAIL","subscribed":True},
{"notificationType":"EXCLUSION_REPORT_SUBMITTED","channel":"IN_APP","subscribed":True},
{"notificationType":"EXCLUSION_REPORT_SUBMITTED","channel":"EMAIL","subscribed":True},
{"notificationType":"EXCLUSION_REPORT_REQUESTED_SUPPLEMENTAL","channel":"IN_APP","subscribed":True},
{"notificationType":"EXCLUSION_REPORT_REQUESTED_SUPPLEMENTAL","channel":"EMAIL","subscribed":True},
{"notificationType":"EXCLUSION_REPORT_ACCEPTED","channel":"IN_APP","subscribed":True},
{"notificationType":"EXCLUSION_REPORT_ACCEPTED","channel":"EMAIL","subscribed":True},
{"notificationType":"EXCLUSION_REPORT_REJECTED","channel":"IN_APP","subscribed":True},
{"notificationType":"EXCLUSION_REPORT_REJECTED","channel":"EMAIL","subscribed":True}
]

notifications_payload_inactive = [
{"notificationType":"CREDIT_TRANSFER_CREATED","channel":"IN_APP","subscribed":False},
{"notificationType":"CREDIT_TRANSFER_CREATED","channel":"EMAIL","subscribed":False},
{"notificationType":"CREDIT_TRANSFER_SIGNED_1OF2","channel":"IN_APP","subscribed":False},
{"notificationType":"CREDIT_TRANSFER_SIGNED_1OF2","channel":"EMAIL","subscribed":False},
{"notificationType":"CREDIT_TRANSFER_PROPOSAL_REFUSED","channel":"IN_APP","subscribed":False},
{"notificationType":"CREDIT_TRANSFER_PROPOSAL_REFUSED","channel":"EMAIL","subscribed":False},
{"notificationType":"CREDIT_TRANSFER_SIGNED_2OF2","channel":"IN_APP","subscribed":False},
{"notificationType":"CREDIT_TRANSFER_SIGNED_2OF2","channel":"EMAIL","subscribed":False},
{"notificationType":"CREDIT_TRANSFER_APPROVED","channel":"IN_APP","subscribed":False},
{"notificationType":"CREDIT_TRANSFER_APPROVED","channel":"EMAIL","subscribed":False},
{"notificationType":"CREDIT_TRANSFER_DECLINED","channel":"IN_APP","subscribed":False},
{"notificationType":"CREDIT_TRANSFER_DECLINED","channel":"EMAIL","subscribed":False},
{"notificationType":"CREDIT_TRANSFER_RESCINDED","channel":"IN_APP","subscribed":False},
{"notificationType":"CREDIT_TRANSFER_RESCINDED","channel":"EMAIL","subscribed":False},
{"notificationType":"PVR_APPROVED","channel":"IN_APP","subscribed":False},
{"notificationType":"PVR_APPROVED","channel":"EMAIL","subscribed":False},
{"notificationType":"PVR_DECLINED","channel":"IN_APP","subscribed":False},
{"notificationType":"PVR_DECLINED","channel":"EMAIL","subscribed":False},
{"notificationType":"DOCUMENT_PENDING_SUBMISSION","channel":"IN_APP","subscribed":False},
{"notificationType":"DOCUMENT_PENDING_SUBMISSION","channel":"EMAIL","subscribed":False},
{"notificationType":"DOCUMENT_SUBMITTED","channel":"IN_APP","subscribed":False},
{"notificationType":"DOCUMENT_SUBMITTED","channel":"EMAIL","subscribed":False},
{"notificationType":"DOCUMENT_RECEIVED","channel":"IN_APP","subscribed":False},
{"notificationType":"DOCUMENT_RECEIVED","channel":"EMAIL","subscribed":False},
{"notificationType":"DOCUMENT_ARCHIVED","channel":"IN_APP","subscribed":False},
{"notificationType":"DOCUMENT_ARCHIVED","channel":"EMAIL","subscribed":False},
{"notificationType":"COMPLIANCE_REPORT_DRAFT","channel":"IN_APP","subscribed":False},
{"notificationType":"COMPLIANCE_REPORT_DRAFT","channel":"EMAIL","subscribed":False},
{"notificationType":"COMPLIANCE_REPORT_SUBMITTED","channel":"IN_APP","subscribed":False},
{"notificationType":"COMPLIANCE_REPORT_SUBMITTED","channel":"EMAIL","subscribed":False},
{"notificationType":"COMPLIANCE_REPORT_REQUESTED_SUPPLEMENTAL","channel":"IN_APP","subscribed":False},
{"notificationType":"COMPLIANCE_REPORT_REQUESTED_SUPPLEMENTAL","channel":"EMAIL","subscribed":False},
{"notificationType":"COMPLIANCE_REPORT_ACCEPTED","channel":"IN_APP","subscribed":False},
{"notificationType":"COMPLIANCE_REPORT_ACCEPTED","channel":"EMAIL","subscribed":False},
{"notificationType":"COMPLIANCE_REPORT_REJECTED","channel":"IN_APP","subscribed":False},
{"notificationType":"COMPLIANCE_REPORT_REJECTED","channel":"EMAIL","subscribed":False},
{"notificationType":"EXCLUSION_REPORT_DRAFT","channel":"IN_APP","subscribed":False},
{"notificationType":"EXCLUSION_REPORT_DRAFT","channel":"EMAIL","subscribed":False},
{"notificationType":"EXCLUSION_REPORT_SUBMITTED","channel":"IN_APP","subscribed":False},
{"notificationType":"EXCLUSION_REPORT_SUBMITTED","channel":"EMAIL","subscribed":False},
{"notificationType":"EXCLUSION_REPORT_REQUESTED_SUPPLEMENTAL","channel":"IN_APP","subscribed":False},
{"notificationType":"EXCLUSION_REPORT_REQUESTED_SUPPLEMENTAL","channel":"EMAIL","subscribed":False},
{"notificationType":"EXCLUSION_REPORT_ACCEPTED","channel":"IN_APP","subscribed":False},
{"notificationType":"EXCLUSION_REPORT_ACCEPTED","channel":"EMAIL","subscribed":False},
{"notificationType":"EXCLUSION_REPORT_REJECTED","channel":"IN_APP","subscribed":False},
{"notificationType":"EXCLUSION_REPORT_REJECTED","channel":"EMAIL","subscribed":False}
]

notifications_payload_single_false = [
{"notificationType":"CREDIT_TRANSFER_CREATED","channel":"EMAIL","subscribed":False}
]
128 changes: 128 additions & 0 deletions backend/api/tests/test_notifications_amqp.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
import json
from unittest.mock import patch, Mock
from django.test import TestCase
from .base_test_case import BaseTestCase
from api.models.Organization import Organization
from api.models.Role import Role
from api.models.User import User
from api.models.NotificationChannel import NotificationChannel
from api.notifications.notification_types import NotificationType
from api.notifications.notifications import AMQPNotificationService, NotificationDeliveryFailure, InvalidNotificationArguments
from api.models.NotificationSubscription import NotificationSubscription
from api.tests.payloads.notification_payloads import notifications_payload_active, notifications_payload_inactive, notifications_payload_single_false

class TestAMQPNotificationService(BaseTestCase):

@patch('api.notifications.notifications.send_amqp_notification')
def setUp(self, mock_send_amqp):
super().setUp()

self.org1 = self.organizations['from']
self.user1 = self.users['fs_user_1']
self.org2 = self.organizations['to']
self.user2 = self.users['fs_user_2']

self.channel1 = NotificationChannel.objects.get(channel="IN_APP")
self.channel2 = NotificationChannel.objects.get(channel="EMAIL")

mock_send_amqp.return_value = True

def test_compute_effective_subscriptions_default(self):
subscriptions = AMQPNotificationService.compute_effective_subscriptions(self.user1)

# There are two channels and each has one notification type in our setup. So 2 subscriptions.
self.assertEqual(len(subscriptions), 132)

def test_update_subscription_active(self):
response = self.clients['fs_user_1'].post(
'/api/notifications/update_subscription',
content_type='application/json',
data=json.dumps(notifications_payload_active)
)

subscription = NotificationSubscription.objects.get(
user=self.user1,
channel=self.channel1,
notification_type=NotificationType.CREDIT_TRANSFER_CREATED
)

self.assertTrue(subscription.enabled)

def test_update_subscription_inactive(self):
response = self.clients['fs_user_1'].post(
'/api/notifications/update_subscription',
content_type='application/json',
data=json.dumps(notifications_payload_inactive)
)

subscription = NotificationSubscription.objects.get(
user=self.user1,
channel=self.channel1,
notification_type=NotificationType.CREDIT_TRANSFER_CREATED
)

self.assertFalse(subscription.enabled)

def test_send_notification_global(self):
response = self.clients['fs_user_1'].post(
'/api/notifications/update_subscription',
content_type='application/json',
data=json.dumps(notifications_payload_active)
)
response = self.clients['fs_user_2'].post(
'/api/notifications/update_subscription',
content_type='application/json',
data=json.dumps(notifications_payload_active)
)
with patch('api.notifications.notifications.AMQPNotificationService.send_email_for_notification') as mock_email:
AMQPNotificationService.send_notification(
message="Test Global Message",
is_global=True,
notification_type=NotificationType.CREDIT_TRANSFER_CREATED,
originating_user=self.user1
)
# Check that the in-app notification was sent to all active users
self.assertEqual(mock_email.call_count, 2)

def test_send_notification_specific_org(self):
response = self.clients['fs_user_1'].post(
'/api/notifications/update_subscription',
content_type='application/json',
data=json.dumps(notifications_payload_active)
)
with patch('api.notifications.notifications.AMQPNotificationService.send_email_for_notification') as mock_email:
AMQPNotificationService.send_notification(
message="Test Message to Org1",
interested_organization=self.org1,
notification_type=NotificationType.CREDIT_TRANSFER_CREATED,
originating_user=self.user2
)
# Check that the in-app notification was sent to all users in the specified organization
self.assertEqual(mock_email.call_count, 1)

def test_invalid_notification_args(self):
with self.assertRaises(InvalidNotificationArguments):
AMQPNotificationService.send_notification(
message=None, # Invalid as message is required
interested_organization=self.org1,
notification_type=NotificationType.CREDIT_TRANSFER_CREATED,
originating_user=self.user1
)

def test_user_does_not_receive_unsubscribed_notification(self):
# Deactivate a particular notification for user1
response = self.clients['fs_user_1'].post(
'/api/notifications/update_subscription',
content_type='application/json',
data=json.dumps(notifications_payload_single_false)
)
with patch('api.notifications.notifications.AMQPNotificationService.send_email_for_notification') as mock_email:
AMQPNotificationService.send_notification(
message="Test Message to Org1",
interested_organization=self.org1,
notification_type=NotificationType.CREDIT_TRANSFER_CREATED,
originating_user=self.user2
)

# Check that the in-app notification was NOT sent to user1
mock_email.assert_not_called()
13 changes: 7 additions & 6 deletions backend/api/viewsets/CreditTrade.py
Original file line number Diff line number Diff line change
Expand Up @@ -146,12 +146,13 @@ def perform_update(self, serializer):
credit_trade = serializer.save()
# we only want to create history and send notifications
# when a status change occurs
if previous_state.status != credit_trade.status:
CreditTradeService.create_history(credit_trade, False)
status_cancelled = CreditTradeStatus.objects.get(status="Cancelled")
if serializer.data['status'] != status_cancelled.id:
CreditTradeService.dispatch_notifications(
previous_state, credit_trade)
if previous_state.status != 'Rescinded':
if previous_state.status != credit_trade.status or credit_trade.is_rescinded:
CreditTradeService.create_history(credit_trade, False)
status_cancelled = CreditTradeStatus.objects.get(status="Cancelled")
if serializer.data['status'] != status_cancelled.id:
CreditTradeService.dispatch_notifications(
previous_state, credit_trade)

@action(detail=True, methods=['put'])
def delete(self, request, pk=None):
Expand Down
2 changes: 1 addition & 1 deletion frontend/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "tfrs",
"version": "2.9.0",
"version": "2.10.0",
"dependencies": {
"@babel/eslint-parser": "^7.19.1",
"@babel/plugin-proposal-object-rest-spread": "^7.20.7",
Expand Down
13 changes: 9 additions & 4 deletions frontend/src/app/components/Tooltip.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,12 +55,17 @@ class Tooltip extends Component {
onMouseOver={this._hover}
onFocus={this._hover}
>
<div className="tooltip-content">
{Array.isArray(this.props.title) &&
this.props.title.map(title => (<div key={title}><ReactMarkdown>{title}</ReactMarkdown></div>))
}
{!Array.isArray(this.props.title) &&
this.props.title.map(title => (
<div key={title}>
<ReactMarkdown>{title}</ReactMarkdown>
</div>
))}
{!Array.isArray(this.props.title) && (
<ReactMarkdown>{this.props.title}</ReactMarkdown>
}
)}
</div>
</ReactTooltip>
)
}
Expand Down
Loading

0 comments on commit 06f401c

Please sign in to comment.