-
Notifications
You must be signed in to change notification settings - Fork 570
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
feat(connect): add initial support for connect component #2280
Changes from all commits
e980158
358c9d4
b6fa60d
ccd3a09
055b030
ccb4b5f
699338f
d75daed
30bb926
b43bfac
97a6a06
743e2ce
0fa02c0
c764222
4990156
93f4dc9
15968cf
a61744a
3b4711b
17698f3
1023547
708c390
4ad56c0
77afd1a
17e9bff
e11089f
169985e
90e2c88
1a570bd
77514e6
92addb5
a1fcb95
43d79d3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
/* @flow */ | ||
import { loadAxo } from "@paypal/connect-loader-component"; | ||
import { stringifyError } from "@krakenjs/belter/src"; | ||
import { | ||
getClientID, | ||
getClientMetadataID, | ||
getUserIDToken, | ||
getLogger, | ||
} from "@paypal/sdk-client/src"; | ||
|
||
import { sendCountMetric } from "./sendCountMetric"; | ||
|
||
// $FlowFixMe | ||
export const getConnectComponent = async (merchantProps) => { | ||
sendCountMetric({ | ||
name: "pp.app.paypal_sdk.connect.init.count", | ||
dimensions: {}, | ||
}); | ||
|
||
const cmid = getClientMetadataID(); | ||
const clientID = getClientID(); | ||
const userIdToken = getUserIDToken(); | ||
const { metadata } = merchantProps; | ||
|
||
let loadResult = {}; | ||
try { | ||
loadResult = await loadAxo({ | ||
platform: "PPCP", | ||
btSdkVersion: "3.97.3-connect-alpha.6.1", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So far this has to stay hardcoded. More to come, though. |
||
minified: true, | ||
metadata, | ||
}); | ||
} catch (error) { | ||
sendCountMetric({ | ||
name: "pp.app.paypal_sdk.connect.init.error.count", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. should this be There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We are keeping all errors under the same event name. Which then will be funneled based on |
||
event: "error", | ||
dimensions: { | ||
errorName: "connect_load_error", | ||
}, | ||
}); | ||
|
||
getLogger().error("load_axo_error", { err: stringifyError(error) }); | ||
|
||
throw new Error(error); | ||
} | ||
|
||
try { | ||
const connect = await window.braintree.connect.create({ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I remember one issue where I was unable to rely on There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We're ok here because the AXO module we load above is what will append that window object. |
||
...loadResult.metadata, // returns a localeURL for assets | ||
...merchantProps, // AXO specific props | ||
platformOptions: { | ||
platform: "PPCP", | ||
userIdToken, | ||
clientID, | ||
clientMetadataID: cmid, | ||
}, | ||
}); | ||
|
||
sendCountMetric({ | ||
name: "pp.app.paypal_sdk.connect.init.success.count", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. did you follow a guideline for this There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @jshawl we referenced the |
||
event: "success", | ||
dimensions: {}, | ||
}); | ||
|
||
return connect; | ||
} catch (error) { | ||
sendCountMetric({ | ||
name: "pp.app.paypal_sdk.connect.init.error.count", | ||
event: "error", | ||
dimensions: { | ||
errorName: "connect_init_error", | ||
}, | ||
}); | ||
|
||
getLogger().error("init_axo_error", { err: stringifyError(error) }); | ||
|
||
throw new Error(error); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we should also log these errors There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @wsbrunson done! |
||
} | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
/* @flow */ | ||
|
||
import { | ||
getClientID, | ||
getClientMetadataID, | ||
getUserIDToken, | ||
} from "@paypal/sdk-client/src"; | ||
import { loadAxo } from "@paypal/connect-loader-component"; | ||
import { describe, expect, test, vi } from "vitest"; | ||
|
||
import { getConnectComponent } from "./component"; | ||
import { sendCountMetric } from "./sendCountMetric"; | ||
|
||
vi.mock("@paypal/sdk-client/src", () => { | ||
return { | ||
getClientID: vi.fn(() => "mock-client-id"), | ||
getClientMetadataID: vi.fn(() => "mock-cmid"), | ||
getUserIDToken: vi.fn(() => "mock-uid"), | ||
getLogger: vi.fn(() => ({ metric: vi.fn(), error: vi.fn() })), | ||
}; | ||
}); | ||
|
||
vi.mock("@paypal/connect-loader-component", () => { | ||
return { | ||
loadAxo: vi.fn(), | ||
}; | ||
}); | ||
|
||
vi.mock("./sendCountMetric", () => { | ||
return { | ||
sendCountMetric: vi.fn(), | ||
}; | ||
}); | ||
|
||
describe("getConnectComponent: returns ConnectComponent", () => { | ||
const mockAxoMetadata = { someData: "data" }; | ||
const mockProps = { someProp: "value" }; | ||
beforeEach(() => { | ||
vi.clearAllMocks(); | ||
window.braintree = { | ||
connect: { | ||
create: vi.fn(), | ||
}, | ||
}; | ||
|
||
loadAxo.mockResolvedValue({ metadata: mockAxoMetadata }); | ||
}); | ||
|
||
test("loadAxo and window.braintree.connect.create are called with proper data", async () => { | ||
await getConnectComponent(mockProps); | ||
|
||
expect(getClientID).toHaveBeenCalled(); | ||
expect(getClientMetadataID).toHaveBeenCalled(); | ||
expect(getUserIDToken).toHaveBeenCalled(); | ||
expect(loadAxo).toHaveBeenCalled(); | ||
|
||
expect(window.braintree.connect.create).toHaveBeenCalledWith({ | ||
...mockAxoMetadata, | ||
...mockProps, | ||
platformOptions: { | ||
platform: "PPCP", | ||
clientID: "mock-client-id", | ||
clientMetadataID: "mock-cmid", | ||
userIdToken: "mock-uid", | ||
}, | ||
}); | ||
expect(sendCountMetric).toBeCalledTimes(2); | ||
}); | ||
|
||
test("loadAxo failure is handled", async () => { | ||
const errorMessage = "Something went wrong"; | ||
loadAxo.mockRejectedValue(errorMessage); | ||
|
||
await expect(() => getConnectComponent(mockProps)).rejects.toThrow( | ||
errorMessage | ||
); | ||
expect(sendCountMetric).toHaveBeenCalledTimes(2); | ||
}); | ||
|
||
test("connect create failure is handled", async () => { | ||
const expectedError = "create failed"; | ||
window.braintree.connect.create.mockRejectedValue(expectedError); | ||
|
||
await expect(() => getConnectComponent(mockProps)).rejects.toThrow( | ||
expectedError | ||
); | ||
expect(sendCountMetric).toBeCalledTimes(2); | ||
}); | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
/* eslint-disable flowtype/no-weak-types */ | ||
/* @flow */ | ||
// flow-disable | ||
|
||
import { getConnectComponent } from "./component"; | ||
|
||
type ConnectComponent = (merchantProps: any) => ConnectComponent; | ||
// $FlowFixMe | ||
export const Connect: ConnectComponent = async ( | ||
merchantProps: any | ||
): ConnectComponent => { | ||
// $FlowFixMe | ||
return await getConnectComponent(merchantProps); | ||
}; | ||
|
||
/* eslint-enable flowtype/no-weak-types */ |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
/* @flow */ | ||
|
||
import { describe, expect, vi } from "vitest"; | ||
|
||
import { getConnectComponent } from "./component"; | ||
import { Connect } from "./interface"; | ||
|
||
describe("interface.js", () => { | ||
vi.mock("./component", () => { | ||
return { | ||
getConnectComponent: vi.fn(), | ||
}; | ||
}); | ||
it("should call getConnectComponent with merchant props", async () => { | ||
const merchantProps = { props: "someProps" }; | ||
await Connect(merchantProps); | ||
expect(getConnectComponent).toBeCalledWith(merchantProps); | ||
}); | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
/* @flow */ | ||
import { getLogger } from "@paypal/sdk-client/src"; | ||
|
||
// TODO: This will be pulled in to a shared sdk-client util | ||
export const sendCountMetric = ({ | ||
dimensions, | ||
event = "unused", | ||
name, | ||
value = 1, | ||
}: {| | ||
event?: string, | ||
name: string, | ||
value?: number, | ||
dimensions: { | ||
[string]: mixed, | ||
}, | ||
// $FlowIssue return type | ||
|}) => | ||
getLogger().metric({ | ||
dimensions, | ||
metricEventName: event, | ||
metricNamespace: name, | ||
metricValue: value, | ||
metricType: "counter", | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should these be camel case? I don't know anymore when it comes to "id".