Skip to content

feat: send Challenge data event to Harmony #69

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

Open
wants to merge 14 commits into
base: main
Choose a base branch
from
18 changes: 18 additions & 0 deletions src/domain/Challenge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ import { V5_TRACK_IDS_TO_NAMES, V5_TYPE_IDS_TO_NAMES } from "../common/Conversio
import PaymentCreator, { PaymentDetail } from "../util/PaymentCreator";
import { getChallengeResources } from "../api/v5Api";
import m2mToken from "../helpers/MachineToMachineToken";
import { sendHarmonyEvent } from "../helpers/Harmony";

if (!process.env.GRPC_ACL_SERVER_HOST || !process.env.GRPC_ACL_SERVER_PORT) {
throw new Error("Missing required configurations GRPC_ACL_SERVER_HOST and GRPC_ACL_SERVER_PORT");
Expand Down Expand Up @@ -267,6 +268,7 @@ class ChallengeDomain extends CoreOperations<Challenge, CreateChallengeInput> {
};

newChallenge = await super.create(challenge, metadata);
await sendHarmonyEvent("CREATE", "Challenge", newChallenge, input.billing?.billingAccountId!);
} catch (err) {
// Rollback lock amount
if (baValidation != null) {
Expand Down Expand Up @@ -536,6 +538,19 @@ class ChallengeDomain extends CoreOperations<Challenge, CreateChallengeInput> {
};

updatedChallenge = await super.update(scanCriteria, dataToUpdate, metadata);

const newChallenge = updatedChallenge.items[0];
if (newChallenge.billing?.billingAccountId !== challenge?.billing?.billingAccountId) {
// For a New/Draft challenge, it might miss billing account id
// However when challenge activates, the billing account id will be provided (challenge-api validates it)
// In such case, send a CREATE event with whole challenge data (it's fine for search-indexer since it upserts for CREATE)
// Otherwise, the outer customer specified by the billing account id (like Topgear) will never receive a challenge CREATE event
await sendHarmonyEvent("CREATE", "Challenge", newChallenge, newChallenge.billing?.billingAccountId);
} else {
// Send only the updated data
// Some field like chanllege description could be big, don't include them if they're not actually updated
await sendHarmonyEvent("UPDATE", "Challenge", { ...dataToUpdate, id: newChallenge.id }, newChallenge.billing?.billingAccountId);
}
} catch (err) {
if (baValidation != null) {
await lockConsumeAmount(baValidation, true);
Expand Down Expand Up @@ -725,12 +740,14 @@ class ChallengeDomain extends CoreOperations<Challenge, CreateChallengeInput> {
if (baValidation != null) await lockConsumeAmount(baValidation);
try {
await super.update(scanCriteria, dynamoUpdate);
await sendHarmonyEvent("UPDATE", "Challenge", { ...data, id }, challenge.billing?.billingAccountId);
} catch (err) {
if (baValidation != null) await lockConsumeAmount(baValidation, true);
throw err;
}
} else {
await super.update(scanCriteria, dynamoUpdate);
await sendHarmonyEvent("UPDATE", "Challenge", { ...data, id }, challenge.billing?.billingAccountId);
console.log("Challenge Completed");

const completedChallenge = await this.lookup(DomainHelper.getLookupCriteria("id", id));
Expand Down Expand Up @@ -805,6 +822,7 @@ class ChallengeDomain extends CoreOperations<Challenge, CreateChallengeInput> {

try {
const result = await super.delete(lookupCriteria);
await sendHarmonyEvent("DELETE", "Challenge", { id: challenge.id }, challenge.billing?.billingAccountId);
return result;
} catch (err) {
await lockConsumeAmount(baValidation, true);
Expand Down
46 changes: 46 additions & 0 deletions src/helpers/Harmony.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import _ from "lodash";
import { InvokeCommand, LambdaClient } from "@aws-sdk/client-lambda";

import { EVENT_ORIGINATOR } from "../common/Constants";

const FunctionName =
process.env.HARMONY_LAMBDA_FUNCTION ??
"arn:aws:lambda:us-east-1:811668436784:function:harmony-api-dev-processMessage";

const harmonyClient = new LambdaClient({ region: process.env.AWS_REGION, maxAttempts: 2 });

/**
* Send event to Harmony.
* @param eventType The event type
* @param payloadType The payload type
* @param payload The event payload
* @param billingAccountId The billing account id
*/
export async function sendHarmonyEvent(eventType: string, payloadType: string, payload: object, billingAccountId?: number) {
const event = {
publisher: EVENT_ORIGINATOR,
timestamp: new Date().getTime(),
eventType,
payloadType,
payload,
billingAccountId,
};

const invokeCommand = new InvokeCommand({
FunctionName,
InvocationType: "RequestResponse",
Payload: JSON.stringify(event),
LogType: "None"
});

const result = await harmonyClient.send(invokeCommand);

if (result.FunctionError) {
console.error(
"Failed to send Harmony event",
result.FunctionError,
result.Payload?.transformToString()
);
throw new Error(result.FunctionError);
}
}