Skip to content

Commit

Permalink
Merge branch 'main' into feat-save-last-connected-organization
Browse files Browse the repository at this point in the history
  • Loading branch information
jbair06 authored Jan 22, 2025
2 parents ebf4a8f + 386c1df commit d4f7adc
Show file tree
Hide file tree
Showing 44 changed files with 674 additions and 474 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/test-backend.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ jobs:
command: notifications
steps:
- name: Harden Runner
uses: step-security/harden-runner@c95a14d0e5bab51a9f56296a4eb0e416910cd350 # v2.10.3
uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
with:
egress-policy: audit

Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/test-frontend.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ jobs:

steps:
- name: Harden Runner
uses: step-security/harden-runner@c95a14d0e5bab51a9f56296a4eb0e416910cd350 # v2.10.3
uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
with:
egress-policy: audit

Expand Down Expand Up @@ -79,7 +79,7 @@ jobs:
command: WorkflowTests
steps:
- name: Harden Runner
uses: step-security/harden-runner@c95a14d0e5bab51a9f56296a4eb0e416910cd350 # v2.10.3
uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
with:
egress-policy: audit

Expand Down Expand Up @@ -145,7 +145,7 @@ jobs:
command: main
steps:
- name: Harden Runner
uses: step-security/harden-runner@c95a14d0e5bab51a9f56296a4eb0e416910cd350 # v2.10.3
uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4
with:
egress-policy: audit

Expand Down
34 changes: 5 additions & 29 deletions automation/pages/OrganizationPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@ class OrganizationPage extends BasePage {
this.users = []; // List to store user credentials
this.transactions = []; // List to store transactions
this.organizationRecoveryWords = []; // List to store recovery phrase words for organization
this.badOrganizationList = []; // List to store bad organizations
this.complexAccountId = []; // List to store complex account ids
this.complexFileId = []; // List to store complex file ids
this.registrationPage = new RegistrationPage(window);
Expand All @@ -72,7 +71,6 @@ class OrganizationPage extends BasePage {
dropdownSelectModeSelector = 'dropdown-select-mode';
dropdownSelectedModeSelector = 'dropdown-selected-mode';
editNicknameOrganizationButtonSelector = 'button-edit-nickname';
closeErrorModalButtonSelector = 'button-close-modal';
logoutButtonSelector = 'button-logout';
contactListButton = 'button-contact-list';
deleteNextButtonSelector = 'button-delete-next';
Expand All @@ -88,6 +86,7 @@ class OrganizationPage extends BasePage {
signTransactionButtonSelector = 'button-sign-org-transaction';
nextTransactionButtonSelector = 'button-next-org-transaction';
signAllTransactionsButtonSelector = 'button-sign-all-tx';
cancelAddingOrganizationButtonSelector = 'button-cancel-adding-org';

// Inputs
organizationNicknameInputSelector = 'input-organization-nickname';
Expand All @@ -98,7 +97,6 @@ class OrganizationPage extends BasePage {
editOrganizationNicknameInputSelector = 'input-edit-nickname';

// Texts
organizationErrorMessageSelector = 'p-organization-error-message';
organizationNicknameTextSelector = 'span-organization-nickname';
transactionDetailsIdSelector = 'p-transaction-details-id';
transactionValidStartSelector = 'p-transaction-details-valid-start';
Expand Down Expand Up @@ -177,7 +175,6 @@ class OrganizationPage extends BasePage {
const serverUrl = process.env.ORGANIZATION_URL + Math.floor(Math.random() * 10);
await this.clickOnAddNewOrganizationButton();
await this.fillOrganizationDetailsAndContinue(organizationNickname, serverUrl);
this.badOrganizationList.push(organizationNickname);
}

/**
Expand Down Expand Up @@ -319,31 +316,10 @@ class OrganizationPage extends BasePage {
return checks.every(isTrue => isTrue);
}

async getOrganizationErrorMessage() {
return await this.getText(this.organizationErrorMessageSelector);
}

async clickOnCloseErrorModalButton() {
await this.click(this.closeErrorModalButtonSelector);
}

async clickOnDeleteSecondOrganization() {
await this.click(this.deleteOrganizationButtonSelector, 1);
}

async clickOnDeleteFirstOrganization() {
await this.click(this.deleteOrganizationButtonSelector, 0);
}

async ensureBadOrganizationExists() {
if (this.badOrganizationList.length === 0) {
await this.settingsPage.clickOnSettingsButton();
await this.settingsPage.clickOnOrganisationsTab();
await this.setupWrongOrganization();
await this.clickOnCloseErrorModalButton();
}
}

async clickOnSelectModeDropdown() {
await this.click(this.dropdownSelectModeSelector);
}
Expand Down Expand Up @@ -1466,10 +1442,6 @@ class OrganizationPage extends BasePage {
return await this.getText(this.observerIndexSelector + index);
}

async getNotificationNumberText() {
return await this.getText(this.spanNotificationNumberSelector);
}

async isNotificationNumberVisible() {
return await this.isElementVisible(this.spanNotificationNumberSelector);
}
Expand Down Expand Up @@ -1505,6 +1477,10 @@ class OrganizationPage extends BasePage {
async isNextTransactionButtonVisible() {
return await this.isElementVisible(this.nextTransactionButtonSelector);
}

async clickOnCancelAddingOrganizationButton() {
await this.click(this.cancelAddingOrganizationButtonSelector);
}
}

module.exports = OrganizationPage;
34 changes: 18 additions & 16 deletions automation/tests/organizationSettingsTests.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -86,24 +86,11 @@ test.describe('Organization Settings tests', () => {
});

test('Verify error message when user adds non-existing organization', async () => {
await organizationPage.setupWrongOrganization();
const errorMessage = (await organizationPage.getOrganizationErrorMessage()).trim();
expect(errorMessage).toBe('Organization server is not reachable');
await organizationPage.clickOnCloseErrorModalButton();
});

test('Verify user can delete an organization', async () => {
await organizationPage.ensureBadOrganizationExists();
await settingsPage.clickOnSettingsButton();
await settingsPage.clickOnOrganisationsTab();
await loginPage.waitForToastToDisappear();
await organizationPage.clickOnDeleteSecondOrganization();

await organizationPage.setupWrongOrganization();
const toastMessage = await registrationPage.getToastMessage();
expect(toastMessage).toBe('Connection deleted successfully');

const isDeletedFromDb = await organizationPage.verifyOrganizationExists('Bad Organization');
expect(isDeletedFromDb).toBe(false);
expect(toastMessage).toBe('Organization does not exist. Please check the server URL');
await organizationPage.clickOnCancelAddingOrganizationButton();
});

test('Verify user is prompted for mnemonic phrase and can recover account when resetting organization', async () => {
Expand Down Expand Up @@ -212,4 +199,19 @@ test.describe('Organization Settings tests', () => {
await transactionPage.clickOnTransactionsMenuButton();
expect(await organizationPage.returnAllTabsVisible()).toBe(true);
});

test('Verify user can delete an organization', async () => {
await organizationPage.selectPersonalMode();
await settingsPage.clickOnSettingsButton();
await settingsPage.clickOnOrganisationsTab();
await loginPage.waitForToastToDisappear();
await organizationPage.clickOnDeleteFirstOrganization();

const toastMessage = await registrationPage.getToastMessage();
expect(toastMessage).toBe('Connection deleted successfully');

const orgName = await organizationPage.getOrganizationNicknameText();
const isDeletedFromDb = await organizationPage.verifyOrganizationExists(orgName);
expect(isDeletedFromDb).toBe(false);
});
});
26 changes: 26 additions & 0 deletions back-end/apps/api/src/transactions/transactions.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,10 @@ import {
MirrorNetworkGRPC,
isTransactionBodyOverMaxSize,
emitExecuteTranasction,
notifyGeneral,
} from '@app/common/utils';
import {
NotificationType,
Transaction,
TransactionApprover,
TransactionSigner,
Expand Down Expand Up @@ -672,6 +674,30 @@ describe('TransactionsService', () => {
);
expect(notifyTransactionAction).toHaveBeenCalledWith(notificationsService);
});

it('should emit notification to the notifiaction service', async () => {
const transaction = {
id: 123,
creatorKey: { userId: 1 },
observers: [{ userId: 2 }],
signers: [{ userId: 3 }],
status: TransactionStatus.WAITING_FOR_SIGNATURES,
};

jest
.spyOn(service, 'getTransactionForCreator')
.mockResolvedValueOnce(transaction as Transaction);

await service.cancelTransaction(123, { id: 1 } as User);

expect(notifyGeneral).toHaveBeenCalledWith(
notificationsService,
NotificationType.TRANSACTION_CANCELLED,
expect.arrayContaining([2, 3]),
expect.any(String),
123,
);
});
});

describe('archiveTransaction', () => {
Expand Down
24 changes: 23 additions & 1 deletion back-end/apps/api/src/transactions/transactions.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,13 @@ import {
FindOperator,
} from 'typeorm';

import { Transaction, TransactionSigner, TransactionStatus, User } from '@entities';
import {
NotificationType,
Transaction,
TransactionSigner,
TransactionStatus,
User,
} from '@entities';

import {
CHAIN_SERVICE,
Expand All @@ -39,6 +45,7 @@ import {
ErrorCodes,
isTransactionBodyOverMaxSize,
emitExecuteTranasction,
notifyGeneral,
} from '@app/common';

import { CreateTransactionDto } from './dto';
Expand Down Expand Up @@ -428,6 +435,21 @@ export class TransactionsService {
notifySyncIndicators(this.notificationsService, transaction.id, TransactionStatus.CANCELED);
notifyTransactionAction(this.notificationsService);

/* Send email notification to all the participants */
await this.attachTransactionApprovers(transaction);
const userIds = new Set<number>();
transaction.approvers?.forEach(a => userIds.add(a.userId));
transaction.signers?.forEach(s => userIds.add(s.userId));
transaction.observers?.forEach(o => userIds.add(o.userId));

notifyGeneral(
this.notificationsService,
NotificationType.TRANSACTION_CANCELLED,
[...userIds],
`A transaction ${transaction.name} has been cancelled.\nTransaction ID: ${transaction.transactionId}\n Netowork: ${transaction.mirrorNetwork}`,
transaction.id,
);

return true;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ export enum NotificationTypeEmailSubjects {
TRANSACTION_WAITING_FOR_SIGNATURES = 'Hedera Transaction Tool | Action Required | Review and Sign Transaction',
TRANSACTION_READY_FOR_EXECUTION = 'Hedera Transaction Tool | Transaction ready for execution',
TRANSACTION_EXECUTED = 'Hedera Transaction Tool | Transaction has been executed',
TRANSACTION_CANCELLED = 'Hedera Transaction Tool | Transaction has been cancelled',
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export enum NotificationType {
TRANSACTION_READY_FOR_EXECUTION = 'TRANSACTION_READY_FOR_EXECUTION',
TRANSACTION_EXECUTED = 'TRANSACTION_EXECUTED',
TRANSACTION_EXPIRED = 'TRANSACTION_EXPIRED',
TRANSACTION_CANCELLED = 'TRANSACTION_CANCELLED',
TRANSACTION_INDICATOR_APPROVE = 'TRANSACTION_INDICATOR_APPROVE',
TRANSACTION_INDICATOR_SIGN = 'TRANSACTION_INDICATOR_SIGN',
TRANSACTION_INDICATOR_EXECUTABLE = 'TRANSACTION_INDICATOR_EXECUTABLE',
Expand Down
19 changes: 18 additions & 1 deletion back-end/libs/common/src/utils/client/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,13 @@ import {
SYNC_INDICATORS,
NotifyClientDto,
NotifyForTransactionDto,
NotifyGeneralDto,
UpdateTransactionStatusDto,
SyncIndicatorsDto,
ExecuteTransactionDto,
NOTIFY_GENERAL,
} from '@app/common';
import { TransactionStatus } from '@entities';
import { NotificationType, TransactionStatus } from '@entities';

/* Chain */
export const emitUpdateTransactionStatus = (client: ClientProxy, id: number) => {
Expand Down Expand Up @@ -50,3 +52,18 @@ export const notifySyncIndicators = (
transactionStatus,
});
};

export const notifyGeneral = (
client: ClientProxy,
type: NotificationType,
userIds: number[],
content: string,
entityId?: number,
) => {
client.emit<undefined, NotifyGeneralDto>(NOTIFY_GENERAL, {
type,
userIds,
content,
entityId,
});
};
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ export enum NotificationType {
TRANSACTION_WAITING_FOR_SIGNATURES = 'TRANSACTION_WAITING_FOR_SIGNATURES',
TRANSACTION_READY_FOR_EXECUTION = 'TRANSACTION_READY_FOR_EXECUTION',
TRANSACTION_EXECUTED = 'TRANSACTION_EXECUTED',
TRANSACTION_CANCELLED = 'TRANSACTION_CANCELLED',
TRANSACTION_EXPIRED = 'TRANSACTION_EXPIRED',
TRANSACTION_INDICATOR_APPROVE = 'TRANSACTION_INDICATOR_APPROVE',
TRANSACTION_INDICATOR_SIGN = 'TRANSACTION_INDICATOR_SIGN',
Expand Down
6 changes: 2 additions & 4 deletions front-end/src/renderer/components/Approvers/ApproverModal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,7 @@ const handleApproverUpdate = (newApprover: TransactionApproverDto) =>
const handleSingleApproverUpdate = (newApprover: TransactionApproverDto[]) =>
(currentSingleApprover.value = newApprover);
const handleDoneClick = async (e: Event) => {
e.preventDefault();
const handleDoneClick = async () => {
if (currentApproverInvalid.value) {
errorModalShow.value = true;
return;
Expand Down Expand Up @@ -172,7 +170,7 @@ const modalContentContainerStyle = { padding: '0 10%', height: '80%' };
<template>
<AppModal :show="show" @update:show="emit('update:show', $event)" class="full-screen-modal">
<div class="p-5 h-100">
<form @submit="handleDoneClick" class="flex-column-100 fill-remaining">
<form @submit.prevent="handleDoneClick" class="flex-column-100 fill-remaining">
<div>
<i class="bi bi-x-lg cursor-pointer" @click="$emit('update:show', false)"></i>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,7 @@ const handleSaveComplexKeyButtonClick = () => {
}
};
const handleSaveButtonClick = async (e: Event) => {
e.preventDefault();
const handleSaveButtonClick = async () => {
if (currentKeyInvalid.value) {
errorModalShow.value = true;
return;
Expand All @@ -77,7 +75,7 @@ const modalContentContainerStyle = { padding: '0 10%', height: '80%' };
<template>
<AppModal :show="show" @update:show="handleShowUpdate" class="full-screen-modal">
<div class="p-5 h-100">
<form @submit="handleSaveButtonClick" class="h-100">
<form @submit.prevent="handleSaveButtonClick" class="h-100">
<div>
<i class="bi bi-x-lg cursor-pointer" @click="$emit('update:show', false)"></i>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,7 @@ const nickname = ref('');
/* Handlers */
const handleShowUpdate = (show: boolean) => emit('update:show', show);
const handleSaveKeyList = async (e: Event) => {
e.preventDefault();
const handleSaveKeyList = async () => {
if (!isUserLoggedIn(user.personal)) {
throw new Error('User is not logged in');
}
Expand All @@ -65,7 +64,7 @@ const handleSaveKeyList = async (e: Event) => {
<div>
<i class="bi bi-x-lg cursor-pointer" @click="handleShowUpdate(false)"></i>
</div>
<form class="mt-3" @submit="handleSaveKeyList">
<form class="mt-3" @submit.prevent="handleSaveKeyList">
<h3 class="text-center text-title text-bold">Enter the nickname</h3>
<div class="form-group mt-5 mb-4">
<label class="form-label">Nickname</label>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,7 @@ const accountIdsList = computed(() => {
/* Handlers */
const handleShowUpdate = (show: boolean) => emit('update:show', show);
const handleInsert = async (e: Event) => {
e.preventDefault();
const handleInsert = async () => {
if (!isAccountId(selectedAccountData.accountId.value)) {
throw new Error('Invalid Account ID');
}
Expand Down Expand Up @@ -87,7 +85,7 @@ onMounted(async () => {
<template>
<AppModal :show="show" @update:show="handleShowUpdate" class="medium-modal">
<div class="p-4">
<form @submit="handleInsert">
<form @submit.prevent="handleInsert">
<div>
<i class="bi bi-x-lg cursor-pointer" @click="$emit('update:show', false)"></i>
</div>
Expand Down
Loading

0 comments on commit d4f7adc

Please sign in to comment.