-
-
Notifications
You must be signed in to change notification settings - Fork 2.7k
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
[Bug]: Unable to add additional injected dependencies to custom fulfillment provider #10487
Comments
Might be we are not configuring loaders properly for providers. We'll look into it.
In this case it should be fine to just init the client in the constructor - any particular reason you wanted to do it in a loader instead?
…On Fri, Dec 6 2024 at 11:54 PM, Bob Kinney < ***@***.*** > wrote:
Package.json file
-----------------
{
"name" : " ship " ,
"version" : " 0.0.1 " ,
"description" : " SHIP for Eight Sleep " ,
"author" : " Eight Sleep " ,
"keywords" : [
" sqlite " ,
" postgres " ,
" typescript " ,
" ecommerce " ,
" headless " ,
" medusa "
],
"scripts" : {
"build" : " medusa build " ,
"start" : " medusa start " ,
"dev" : " medusa develop " ,
"test:integration:http" : " TEST_TYPE=integration:http
NODE_OPTIONS=--experimental-vm-modules jest --silent=false --runInBand
--forceExit " ,
"test:integration:modules" : " TEST_TYPE=integration:modules
NODE_OPTIONS=--experimental-vm-modules jest --silent --runInBand
--forceExit " ,
"test:unit" : " TEST_TYPE=unit NODE_OPTIONS=--experimental-vm-modules
jest --silent --runInBand --forceExit "
},
"dependencies" : {
***@***.***/logging" : " ^2.2.0 " ,
***@***.***/practices" : " ^7.16.16 " ,
***@***.***/admin-sdk" : " 2.1.0 " ,
***@***.***/api-key" : " 2.1.0 " ,
***@***.***/auth" : " 2.1.0 " ,
***@***.***/auth-emailpass" : " 2.1.0 " ,
***@***.***/cache-inmemory" : " 2.1.0 " ,
***@***.***/cart" : " 2.1.0 " ,
***@***.***/cli" : " 2.1.0 " ,
***@***.***/currency" : " 2.1.0 " ,
***@***.***/customer" : " 2.1.0 " ,
***@***.***/event-bus-local" : " 2.1.0 " ,
***@***.***/event-bus-redis" : " 2.1.0 " ,
***@***.***/file" : " 2.1.0 " ,
***@***.***/file-local" : " 2.1.0 " ,
***@***.***/framework" : " 2.1.0 " ,
***@***.***/fulfillment" : " 2.1.0 " ,
***@***.***/fulfillment-manual" : " 2.1.0 " ,
***@***.***/inventory" : " 2.1.0 " ,
***@***.***/js-sdk" : " ^2.1.0 " ,
***@***.***/medusa" : " 2.1.0 " ,
***@***.***/notification" : " 2.1.0 " ,
***@***.***/notification-local" : " 2.1.0 " ,
***@***.***/order" : " 2.1.0 " ,
***@***.***/payment" : " 2.1.0 " ,
***@***.***/pricing" : " 2.1.0 " ,
***@***.***/product" : " 2.1.0 " ,
***@***.***/promotion" : " 2.1.0 " ,
***@***.***/region" : " 2.1.0 " ,
***@***.***/sales-channel" : " 2.1.0 " ,
***@***.***/stock-location" : " 2.1.0 " ,
***@***.***/store" : " 2.1.0 " ,
***@***.***/tax" : " 2.1.0 " ,
***@***.***/types" : " 2.1.0 " ,
***@***.***/user" : " 2.1.0 " ,
***@***.***/workflow-engine-inmemory" : " 2.1.0 " ,
"express" : " ^4.21.2 " ,
"stripe" : " ^17.4.0 " ,
"types-joi" : " ^2.1.0 "
},
"devDependencies" : {
***@***.***/test-utils" : " 2.1.0 " ,
***@***.***/cli" : " 5.9.7 " ,
***@***.***/core" : " 5.9.7 " ,
***@***.***/migrations" : " 5.9.7 " ,
***@***.***/postgresql" : " 5.9.7 " ,
***@***.***/number-float64-base-normalize" : " 0.2.3 " ,
***@***.***/core" : " 1.10.0 " ,
***@***.***/jest" : " ^0.2.37 " ,
***@***.***/express" : " ^5.0.0 " ,
***@***.***/jest" : " ^29.5.14 " ,
***@***.***/node" : " ^22.10.1 " ,
***@***.***/react" : " ^18.3.12 " ,
"jest" : " ^29.7.0 " ,
"prop-types" : " ^15.8.1 " ,
"ts-node" : " ^10.9.2 " ,
"typescript" : " ^5.7.2 "
},
"engines" : {
"node" : " >=20 "
}
}
Node.js version
---------------
v20.15.0
Database and its version
------------------------
Aurora Postfres
Operating system name and version
---------------------------------
Mac OS X 24.1.0
Browser name
------------
N/A
What happended?
---------------
As part of my custom fulfillment provider's service I have a loader that
creates an API client and registers it with the container.
const serverlessAuthApi = new ServerlessAuthApi({ baseUrl:
config.get<string>("serverlessAuthApi.host") });
const trawlerApi =
new TrawlerApi({
...
});
container.register("trawlerApi",
asValue(trawlerApi));
But attempts to use that the api as an injected dependency fail. If I use
the following format:
constructor(
{ logger, trawler } : InjectedDependencies,
options: Options
) {
super();
this.logger_ = logger;
this.trawlerApi_ = trawlerApi;
this.options_ = options;
}
I just get an error that my provider can't be loaded, if I use the
following format:
constructor(
dependencies : InjectedDependencies,
options: Options
) {
super();
this.logger_ =
dependencies.logger;
this.trawlerApi_ = dependencies.trawlerApi;
this.options_ = options;
}
I get the real error:
Uncaught AwilixResolutionError AwilixResolutionError: Could not resolve
'trawlerApi'.
Resolution path: trawlerApi
at resolve
(/Users/turacma/Workspace/ship/node_modules/awilix/lib/container.js:252:23)
at get
(/Users/turacma/Workspace/ship/node_modules/awilix/lib/container.js:66:33)
at eval (repl:1:14)
I thought maybe it was a compatibility issue with the client, but I tried
simply regisering a string value, and i get the same result.
Expected behavior
-----------------
Dependencies registered in the loader would be available for use in the
constructor of the service.
Actual behavior
---------------
Provider fails to load.
Link to reproduction repo
-------------------------
See minimal code above
—
Reply to this email directly, view it on GitHub (
#10487 ) , or unsubscribe (
https://github.com/notifications/unsubscribe-auth/ABZUJJXNYKBGARP7ZYZJXID2EITMDAVCNFSM6AAAAABTFRUUXSVHI2DSMVQWIX3LMV43ASLTON2WKOZSG4ZDIMBZGAZTAMA
).
You are receiving this because you are subscribed to this thread. Message
ID: <medusajs/medusa/issues/10487 @ github. com>
|
Yep, this is the work around I'm using. Was just trying to use a loader as it was recommended in the documentation for persistent connections. FWIW, the loader is executing, but nothing I register in the loader is available for consumption. |
Can't seem to assess the they query engine either, which I was trying to access to work around the fact that the |
Same here, for me it is a custom payment provider |
I will share my code too |
if you take out the query, it will work with absolutely no problem. I even had to take it out of the workflow. If you add back query(like below) you get error that says the entire payment provider is not found. But take it out and it works. Also wanted to point out i tried container registration keys, without the keys, resolving from string, both of the helper steps in workflows(useRemoteQuery & useQueryGraph). They did not work, which is why i am kind of confused on logger is working since it is also a core feature. import { AbstractPaymentProvider } from "@medusajs/framework/utils";
import {
CreatePaymentProviderSession,
PaymentProviderError,
PaymentProviderSessionResponse,
PaymentSessionStatus,
Logger,
Query,
MedusaContainer,
PaymentProviderContext,
} from "@medusajs/framework/types";
import { handleTransactionWorkflow } from "../../workflows/wallet/handle-transaction/handle-transaction-workflow";
import { TransactionType } from "../../modules/wallet/models/transaction";
import { ContainerRegistrationKeys } from "@medusajs/framework/utils";
type InjectedDependencies = {
logger: Logger;
query: Query;
};
class TestPaymentProviderService extends AbstractPaymentProvider {
static identifier = "test_payment";
protected logger_: Logger;
protected query_: Query;
constructor({ logger, query }: InjectedDependencies) {
super(arguments[0]);
this.logger_ = logger;
this.query_ = query; // Assign the injected query service to a class property
this.logger_.info(
"TestPaymentProviderService initialized with query dependency.",
);
}
async initiatePayment(
input: CreatePaymentProviderSession,
): Promise<PaymentProviderError | PaymentProviderSessionResponse> {
const { amount, currency_code, context } = input;
this.logger_.info(
JSON.stringify({
message: "Initiating payment session",
data: {
amount,
currency_code,
context,
},
}),
);
const okie = this.query_.graph({
entity: "wallet",
fields: ["id", "balance"],
});
this.logger_.info(
JSON.stringify({
message: "Just did a query lets see",
data: {
okie,
},
}),
);
// Store customer_id in the session data since it's available in context here
return {
data: {
amount,
currency_code,
customer_id: context.customer?.id,
},
};
}
async authorizePayment(
paymentSessionData: Record<string, unknown>,
context: PaymentProviderContext,
): Promise<{
status: PaymentSessionStatus;
data: Record<string, unknown>;
}> {
try {
this.logger_.info(
JSON.stringify({
message: "Authorizing payment",
step: "start",
data: paymentSessionData,
context,
}),
);
const amount = Number(paymentSessionData.amount);
if (isNaN(amount)) {
throw new Error("Invalid amount in payment session data");
}
const customerId = paymentSessionData.customer_id as string;
if (!customerId) {
throw new Error("Customer ID is required in session data");
}
const container = this.container as unknown as MedusaContainer;
const result = await handleTransactionWorkflow(container).run({
input: {
customer_id: customerId,
amount,
type: TransactionType.DEBIT,
description: "Test payment authorization",
metadata: {
payment_method: "test_payment",
},
},
});
this.logger_.info(
JSON.stringify({
message: "Authorizing payment",
step: "after_workflow",
data: { result },
}),
);
if (!result) {
return {
status: "error",
data: {
message: "Wallet not found",
code: "not_found",
},
};
}
return {
status: "authorized",
data: paymentSessionData,
};
} catch (e: any) {
this.logger_.error(
JSON.stringify({
message: "Error in authorizePayment",
error: e.message,
stack: e.stack,
payment_session_data: paymentSessionData,
}),
);
return {
status: "error",
data: {
message: e.message || "Failed to authorize payment",
code: "authorization_failed",
},
};
}
}
async capturePayment(
paymentData: Record<string, unknown>,
): Promise<Record<string, unknown>> {
try {
const amount = Number(paymentData.amount);
if (isNaN(amount)) {
throw new Error("Invalid amount in payment data");
}
const customerId = paymentData.customer_id as string;
if (!customerId) {
throw new Error("Customer ID is required");
}
const container = this.container as unknown as MedusaContainer;
const result = await handleTransactionWorkflow(container).run({
input: {
customer_id: customerId,
amount,
type: TransactionType.DEBIT,
description: "Test payment capture",
metadata: {
payment_method: "test_payment",
},
},
});
this.logger_.info(JSON.stringify({ message: "Workflow result", result }));
return {
...paymentData,
};
} catch (e: any) {
this.logger_.error(
JSON.stringify({
message: "Error in capturePayment",
error: e.message,
stack: e.stack,
}),
);
return {
error: e.message || "Failed to capture payment",
code: "capture_failed",
detail: e.message,
};
}
}
async refundPayment(
paymentData: Record<string, unknown>,
): Promise<Record<string, unknown>> {
try {
const amount = Number(paymentData.amount);
if (isNaN(amount)) {
throw new Error("Invalid amount in payment data");
}
const customerId = paymentData.customer_id as string;
if (!customerId) {
throw new Error("Customer ID is required");
}
const container = this.container as unknown as MedusaContainer;
const result = await handleTransactionWorkflow(container).run({
input: {
customer_id: customerId,
amount,
type: TransactionType.CREDIT,
description: "Test payment refund",
metadata: {
payment_method: "test_payment",
},
},
});
this.logger_.info(JSON.stringify({ message: "Workflow result", result }));
return {
...paymentData,
};
} catch (e: any) {
this.logger_.error(
JSON.stringify({
message: "Error in refundPayment",
error: e.message,
stack: e.stack,
}),
);
return {
error: e.message || "Failed to refund payment",
code: "refund_failed",
detail: e.message,
};
}
}
async cancelPayment(
paymentData: Record<string, unknown>,
): Promise<Record<string, unknown>> {
return {};
}
async deletePayment(
paymentData: Record<string, unknown>,
): Promise<Record<string, unknown>> {
return {};
}
async retrievePayment(
paymentData: Record<string, unknown>,
): Promise<Record<string, unknown>> {
return {};
}
async updatePayment(
sessionData: Record<string, unknown>,
): Promise<PaymentProviderSessionResponse> {
return { data: {} };
}
async getPaymentStatus(
paymentData: Record<string, unknown>,
): Promise<PaymentSessionStatus> {
return "authorized";
}
async getWebhookActionAndData() {
return null;
}
}
export default TestPaymentProviderService; |
@srindom any suggestions on work arounds? Not having access to the rest of the customer information is blocker for some of our integrations. |
@turacma, you can specify a module service's injected dependencies in the module config. Can you try to specify import { loadEnv, defineConfig } from '@medusajs/framework/utils'
loadEnv(process.env.NODE_ENV || 'development', process.cwd())
module.exports = defineConfig({
projectConfig: {
databaseUrl: process.env.DATABASE_URL,
http: { ... },
},
modules: [
{
resolve: "@medusajs/fulfillment",
dependencies: ["trawlerApi"], // <----------- Try to specify here it
options: {
providers: [{ ... }]
}
}
]
}) |
If you have a small reproduction in repo, I am happy to take a look at it |
@olivermrbl that seems to work for |
@turacma How did you get it working for query? This didn't work for me: |
My code: { |
neither did this: |
|
@erickirt the payment module may behave differently. I simply added
|
Package.json file
Node.js version
v20.15.0
Database and its version
Aurora Postfres
Operating system name and version
Mac OS X 24.1.0
Browser name
N/A
What happended?
As part of my custom fulfillment provider's service I have a loader that creates an API client and registers it with the container.
But attempts to use that the api as an injected dependency fail. If I use the following format:
I just get an error that my provider can't be loaded, if I use the following format:
I get the real error:
I thought maybe it was a compatibility issue with the client, but I tried simply regisering a string value, and i get the same result.
Expected behavior
Dependencies registered in the loader would be available for use in the constructor of the service.
Actual behavior
Provider fails to load.
Link to reproduction repo
See minimal code above
The text was updated successfully, but these errors were encountered: