Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/beta' into feature/JST-917/netwo…
Browse files Browse the repository at this point in the history
…rk-module
  • Loading branch information
mgordel committed May 21, 2024
2 parents 0b00200 + 6db5d84 commit b5baad3
Show file tree
Hide file tree
Showing 15 changed files with 258 additions and 64 deletions.
3 changes: 2 additions & 1 deletion src/activity/exe-script-executor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import retry from "async-retry";
import { Result, ResultData, StreamingBatchEvent } from "./results";
import sleep from "../shared/utils/sleep";
import { Activity } from "./activity";
import { getMessageFromApiError } from "../shared/utils/apiErrorMessage";

export interface ExeScriptRequest {
text: string;
Expand Down Expand Up @@ -80,7 +81,7 @@ export class ExeScriptExecutor {
? this.streamingBatch(batchId, batchSize, startTime, timeout)
: this.pollingBatch(batchId, startTime, timeout, maxRetries);
} catch (error) {
const message = error?.response?.data?.message || error.message || error;
const message = getMessageFromApiError(error);

this.logger.error("Execution of script failed.", {
reason: message,
Expand Down
2 changes: 2 additions & 0 deletions src/market/error.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { GolemModuleError } from "../shared/error/golem-error";

export enum MarketErrorCode {
CouldNotGetAgreement = "CouldNotGetAgreement",
CouldNotGetProposal = "CouldNotGetProposal",
ServiceNotInitialized = "ServiceNotInitialized",
MissingAllocation = "MissingAllocation",
SubscriptionFailed = "SubscriptionFailed",
Expand Down
15 changes: 9 additions & 6 deletions src/payment/agreement_payment_process.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import AsyncLock from "async-lock";
import { GolemPaymentError, PaymentErrorCode } from "./error";
import { GolemUserError } from "../shared/error/golem-error";
import { IPaymentApi } from "./types";
import { getMessageFromApiError } from "../shared/utils/apiErrorMessage";

/**
* Process manager that controls the logic behind processing events related to an agreement which result with payments
Expand Down Expand Up @@ -125,10 +126,10 @@ export class AgreementPaymentProcess {

return true;
} catch (error) {
const reason = error?.response?.data?.message || error;
const message = getMessageFromApiError(error);
// this.events.emit("paymentFailed", { id: this.id, agreementId: this.agreementId, reason });
throw new GolemPaymentError(
`Unable to accept debit note ${debitNote.id}. ${reason}`,
`Unable to accept debit note ${debitNote.id}. ${message}`,
PaymentErrorCode.DebitNoteAcceptanceFailed,
undefined,
debitNote.provider,
Expand All @@ -149,8 +150,9 @@ export class AgreementPaymentProcess {
// await this.paymentApi.rejectDebitNote(debitNote, rejectMessage);
this.logger.warn(`DebitNote rejected`, { reason: rejectMessage });
} catch (error) {
const message = getMessageFromApiError(error);
throw new GolemPaymentError(
`Unable to reject debit note ${debitNote.id}. ${error?.response?.data?.message || error}`,
`Unable to reject debit note ${debitNote.id}. ${message}`,
PaymentErrorCode.DebitNoteRejectionFailed,
undefined,
debitNote.provider,
Expand Down Expand Up @@ -221,12 +223,12 @@ export class AgreementPaymentProcess {
// provider: invoice.provider,
// });
} catch (error) {
const reason = error?.response?.data?.message || error;
const message = getMessageFromApiError(error);

// this.events.emit("paymentFailed", { invoiceId: invoice.id, agreementId: invoice.agreementId, reason });

throw new GolemPaymentError(
`Unable to accept invoice ${invoice.id} ${reason}`,
`Unable to accept invoice ${invoice.id} ${message}`,
PaymentErrorCode.InvoiceAcceptanceFailed,
undefined,
invoice.provider,
Expand All @@ -246,8 +248,9 @@ export class AgreementPaymentProcess {
await this.paymentApi.rejectInvoice(invoice, message);
this.logger.warn(`Invoice rejected`, { reason: message });
} catch (error) {
const message = getMessageFromApiError(error);
throw new GolemPaymentError(
`Unable to reject invoice ${invoice.id} ${error?.response?.data?.message || error}`,
`Unable to reject invoice ${invoice.id} ${message}`,
PaymentErrorCode.InvoiceRejectionFailed,
undefined,
invoice.provider,
Expand Down
2 changes: 2 additions & 0 deletions src/payment/error.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ export enum PaymentErrorCode {
DebitNoteAcceptanceFailed = "DebitNoteAcceptanceFailed",
InvoiceRejectionFailed = "InvoiceRejectionFailed",
DebitNoteRejectionFailed = "DebitNoteRejectionFailed",
CouldNotGetDebitNote = "CouldNotGetDebitNote",
CouldNotGetInvoice = "CouldNotGetInvoice",
PaymentStatusQueryFailed = "PaymentStatusQueryFailed",
AgreementAlreadyPaid = "AgreementAlreadyPaid",
InvoiceAlreadyReceived = "InvoiceAlreadyReceived",
Expand Down
22 changes: 22 additions & 0 deletions src/shared/utils/apiErrorMessage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import YaTsClient from "ya-ts-client";
function isApiError(error: unknown): error is YaTsClient.ActivityApi.ApiError {
return typeof error == "object" && error !== null && "name" in error && error.name === "ApiError";
}
/**
* Try to extract a message from a yagna API error.
* If the error is not an instance of `ApiError`, return the error message.
*/
export function getMessageFromApiError(error: unknown): string {
if (!(error instanceof Error)) {
return String(error);
}

if (isApiError(error)) {
try {
return JSON.stringify(error.body, null, 2);
} catch (_jsonParseError) {
return error.message;
}
}
return error.message;
}
41 changes: 32 additions & 9 deletions src/shared/yagna/adapters/activity-api-adapter.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { Agreement } from "../../../agreement";
import { ActivityApi } from "ya-ts-client";
import { Activity, ActivityStateEnum, IActivityApi } from "../../../activity";
import { Activity, ActivityStateEnum, GolemWorkError, IActivityApi, WorkErrorCode } from "../../../activity";
import { IActivityRepository } from "../../../activity/activity";
import { getMessageFromApiError } from "../../utils/apiErrorMessage";

export class ActivityApiAdapter implements IActivityApi {
constructor(
Expand All @@ -15,19 +16,41 @@ export class ActivityApiAdapter implements IActivityApi {
}

async createActivity(agreement: Agreement): Promise<Activity> {
// TODO: Use options
// @ts-expect-error: FIXME #yagna ts types
const { activityId } = await this.control.createActivity({
agreementId: agreement.id,
});
try {
// TODO: Use options
// @ts-expect-error: FIXME #yagna ts types
const { activityId } = await this.control.createActivity({
agreementId: agreement.id,
});

return this.activityRepo.getById(activityId);
return this.activityRepo.getById(activityId);
} catch (error) {
const message = getMessageFromApiError(error);
throw new GolemWorkError(
`Failed to create activity: ${message}`,
WorkErrorCode.ActivityCreationFailed,
agreement,
undefined,
agreement.getProviderInfo(),
);
}
}

async destroyActivity(activity: Activity): Promise<Activity> {
await this.control.destroyActivity(activity.id, 30);
try {
await this.control.destroyActivity(activity.id, 30);

return this.activityRepo.getById(activity.id);
return this.activityRepo.getById(activity.id);
} catch (error) {
const message = getMessageFromApiError(error);
throw new GolemWorkError(
`Failed to destroy activity: ${message}`,
WorkErrorCode.ActivityDestroyingFailed,
activity.agreement,
activity,
activity.agreement.getProviderInfo(),
);
}
}

async getActivityState(id: string): Promise<ActivityStateEnum> {
Expand Down
7 changes: 5 additions & 2 deletions src/shared/yagna/adapters/agreement-api-adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { withTimeout } from "../../utils/timeout";
import { Logger } from "../../utils";
import { AgreementApiConfig } from "../../../agreement";
import { GolemUserError } from "../../error/golem-error";
import { getMessageFromApiError } from "../../utils/apiErrorMessage";

export class AgreementApiAdapter implements IAgreementApi {
constructor(
Expand Down Expand Up @@ -73,8 +74,9 @@ export class AgreementApiAdapter implements IAgreementApi {

return this.repository.getById(agreementId);
} catch (error) {
const message = getMessageFromApiError(error);
throw new GolemMarketError(
`Unable to create agreement ${error?.response?.data?.message || error?.response?.data || error}`,
`Unable to create agreement ${message}`,
MarketErrorCode.LeaseProcessCreationFailed,
error,
);
Expand Down Expand Up @@ -134,8 +136,9 @@ export class AgreementApiAdapter implements IAgreementApi {

return this.repository.getById(agreement.id);
} catch (error) {
const message = getMessageFromApiError(error);
throw new GolemMarketError(
`Unable to terminate agreement ${agreement.id}. ${error.response?.data?.message || error.response?.data || error}`,
`Unable to terminate agreement ${agreement.id}. ${message}`,
MarketErrorCode.LeaseProcessTerminationFailed,
error,
);
Expand Down
4 changes: 3 additions & 1 deletion src/shared/yagna/adapters/market-api-adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import YaTsClient from "ya-ts-client";
import { GolemInternalError } from "../../error/golem-error";
import { Logger } from "../../utils";
import { DemandBodyPrototype, DemandPropertyValue } from "../../../market/demand/demand-body-builder";
import { getMessageFromApiError } from "../../utils/apiErrorMessage";

/**
* A bit more user-friendly type definition of DemandOfferBaseDTO from ya-ts-client
Expand Down Expand Up @@ -124,8 +125,9 @@ export class MarketApiAdapter implements MarketApi {

this.logger.debug("Proposal rejection result from yagna", { response: result });
} catch (error) {
const message = getMessageFromApiError(error);
throw new GolemMarketError(
`Failed to reject proposal. ${error?.response?.data?.message || error}`,
`Failed to reject proposal. ${message}`,
MarketErrorCode.ProposalRejectionFailed,
error,
);
Expand Down
91 changes: 67 additions & 24 deletions src/shared/yagna/adapters/payment-api-adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
import { IInvoiceRepository } from "../../../payment/invoice";
import { Logger, YagnaApi } from "../../utils";
import { IDebitNoteRepository } from "../../../payment/debit_note";
import { getMessageFromApiError } from "../../utils/apiErrorMessage";

export class PaymentApiAdapter implements IPaymentApi {
public receivedInvoices$ = new Subject<Invoice>();
Expand Down Expand Up @@ -76,50 +77,91 @@ export class PaymentApiAdapter implements IPaymentApi {
}

async acceptInvoice(invoice: Invoice, allocation: Allocation, amount: string): Promise<Invoice> {
await this.yagna.payment.acceptInvoice(invoice.id, {
totalAmountAccepted: amount,
allocationId: allocation.id,
});
try {
await this.yagna.payment.acceptInvoice(invoice.id, {
totalAmountAccepted: amount,
allocationId: allocation.id,
});

return this.invoiceRepo.getById(invoice.id);
return this.invoiceRepo.getById(invoice.id);
} catch (error) {
const message = getMessageFromApiError(error);
throw new GolemPaymentError(
`Could not accept invoice. ${message}`,
PaymentErrorCode.InvoiceAcceptanceFailed,
allocation,
invoice.provider,
);
}
}

async rejectInvoice(invoice: Invoice, reason: string): Promise<Invoice> {
await this.yagna.payment.rejectInvoice(invoice.id, {
rejectionReason: "BAD_SERVICE",
totalAmountAccepted: "0.00",
message: reason,
});
try {
await this.yagna.payment.rejectInvoice(invoice.id, {
rejectionReason: "BAD_SERVICE",
totalAmountAccepted: "0.00",
message: reason,
});

return this.invoiceRepo.getById(invoice.id);
return this.invoiceRepo.getById(invoice.id);
} catch (error) {
const message = getMessageFromApiError(error);
throw new GolemPaymentError(
`Could not reject invoice. ${message}`,
PaymentErrorCode.InvoiceRejectionFailed,
undefined,
invoice.provider,
);
}
}

async acceptDebitNote(debitNote: DebitNote, allocation: Allocation, amount: string): Promise<DebitNote> {
await this.yagna.payment.acceptDebitNote(debitNote.id, {
totalAmountAccepted: amount,
allocationId: allocation.id,
});
try {
await this.yagna.payment.acceptDebitNote(debitNote.id, {
totalAmountAccepted: amount,
allocationId: allocation.id,
});

return this.debitNoteRepo.getById(debitNote.id);
return this.debitNoteRepo.getById(debitNote.id);
} catch (error) {
const message = getMessageFromApiError(error);
throw new GolemPaymentError(
`Could not accept debit note. ${message}`,
PaymentErrorCode.DebitNoteAcceptanceFailed,
allocation,
debitNote.provider,
);
}
}

async rejectDebitNote(debitNote: DebitNote, reason: string): Promise<DebitNote> {
await this.yagna.payment.rejectDebitNote(debitNote.id, {
rejectionReason: "BAD_SERVICE",
totalAmountAccepted: "0.00",
message: reason,
});
try {
await this.yagna.payment.rejectDebitNote(debitNote.id, {
rejectionReason: "BAD_SERVICE",
totalAmountAccepted: "0.00",
message: reason,
});

return this.debitNoteRepo.getById(debitNote.id);
return this.debitNoteRepo.getById(debitNote.id);
} catch (error) {
const message = getMessageFromApiError(error);
throw new GolemPaymentError(
`Could not reject debit note. ${message}`,
PaymentErrorCode.DebitNoteRejectionFailed,
undefined,
debitNote.provider,
);
}
}

async getAllocation(id: string) {
try {
const model = await this.yagna.payment.getAllocation(id);
return new Allocation(model);
} catch (error) {
const message = getMessageFromApiError(error);
throw new GolemPaymentError(
`Could not retrieve allocation. ${error.response?.data?.message || error.response?.data || error}`,
`Could not retrieve allocation. ${message}`,
PaymentErrorCode.AllocationCreationFailed,
undefined,
undefined,
Expand Down Expand Up @@ -154,8 +196,9 @@ export class PaymentApiAdapter implements IPaymentApi {

return allocation;
} catch (error) {
const message = getMessageFromApiError(error);
throw new GolemPaymentError(
`Could not create new allocation. ${error.response?.data?.message || error.response?.data || error}`,
`Could not create new allocation. ${message}`,
PaymentErrorCode.AllocationCreationFailed,
undefined,
undefined,
Expand Down
Loading

0 comments on commit b5baad3

Please sign in to comment.