Skip to content
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

Add fetchRelatedObject to V2 Events if needed #2201

Merged
merged 12 commits into from
Oct 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion examples/snippets/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"license": "ISC",
"dependencies": {
"express": "^4.21.0",
"stripe": "file:../../",
"stripe": "file:../..",
"ts-node": "^10.9.2",
"typescript": "^5.6.2"
}
Expand Down
5 changes: 2 additions & 3 deletions examples/snippets/stripe_webhook_handler.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,10 @@ app.post(
// Fetch the event data to understand the failure
const event = await client.v2.core.events.retrieve(thinEvent.id);
if (event.type == 'v1.billing.meter.error_report_triggered') {
const meter = await client.billing.meters.retrieve(
event.related_object.id
);
const meter = await event.fetchRelatedObject();
const meterId = meter.id;
console.log(`Success! ${meterId}`);

// Record the failures and alert your team
// Add your logic here
}
Expand Down
8 changes: 4 additions & 4 deletions examples/snippets/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,9 @@
integrity sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==

"@types/node@>=8.1.0":
version "22.6.1"
resolved "https://registry.yarnpkg.com/@types/node/-/node-22.6.1.tgz#e531a45f4d78f14a8468cb9cdc29dc9602afc7ac"
integrity sha512-V48tCfcKb/e6cVUigLAaJDAILdMP0fUW6BidkPK4GpGjXcfbnoHasCZDwz3N3yVt5we2RHm4XTQCpv0KJz9zqw==
version "22.7.5"
resolved "https://registry.yarnpkg.com/@types/node/-/node-22.7.5.tgz#cfde981727a7ab3611a481510b473ae54442b92b"
integrity sha512-jML7s2NAzMWc//QSJ1a3prpk78cOPchGvXJsC3C6R6PSMoooztvRVQEz89gmBTBY1SPMaqo5teB4uNHPdetShQ==
dependencies:
undici-types "~6.19.2"

Expand Down Expand Up @@ -524,7 +524,7 @@ [email protected]:
integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==

"stripe@file:../..":
version "16.12.0"
version "17.0.0"
dependencies:
"@types/node" ">=8.1.0"
qs "^6.11.0"
Expand Down
68 changes: 60 additions & 8 deletions src/resources/V2/Core/Events.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,64 @@
// File generated from our OpenAPI spec

// This file is manually maintained
import {StripeResource} from '../../../StripeResource.js';

const stripeMethod = StripeResource.method;

export const Events = StripeResource.extend({
retrieve: stripeMethod({method: 'GET', fullPath: '/v2/core/events/{id}'}),
list: stripeMethod({
method: 'GET',
fullPath: '/v2/core/events',
methodType: 'list',
}),
retrieve(...args: any[]) {
const transformResponseData = (response: any): any => {
return this.addFetchRelatedObjectIfNeeded(response);
};
return stripeMethod({
method: 'GET',
fullPath: '/v2/core/events/{id}',
transformResponseData,
}).apply(this, args);
},

list(...args: any[]) {
const transformResponseData = (response: any): any => {
return {
...response,
data: response.data.map(this.addFetchRelatedObjectIfNeeded.bind(this)),
};
};
return stripeMethod({
method: 'GET',
fullPath: '/v2/core/events',
methodType: 'list',
transformResponseData,
}).apply(this, args);
},

/**
* @private
*
* For internal use in stripe-node.
*
* @param pulledEvent The retrieved event object
* @returns The retrieved event object with a fetchRelatedObject method,
* if pulledEvent.related_object is valid (non-null and has a url)
*/
addFetchRelatedObjectIfNeeded(pulledEvent: any) {
if (!pulledEvent.related_object || !pulledEvent.related_object.url) {
ramya-stripe marked this conversation as resolved.
Show resolved Hide resolved
return pulledEvent;
}
return {
...pulledEvent,
fetchRelatedObject: (): Promise<null | any> =>
// call stripeMethod with 'this' resource to fetch
// the related object. 'this' is needed to construct
// and send the request, but the method spec controls
// the url endpoint and method, so it doesn't matter
// that 'this' is an Events resource object here
stripeMethod({
method: 'GET',
fullPath: pulledEvent.related_object.url,
}).apply(this, [
{
stripeAccount: pulledEvent.context,
},
]),
};
},
});
209 changes: 209 additions & 0 deletions test/resources/V2/Core/Events.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
'use strict';

const testUtils = require('../../../testUtils.js');
const expect = require('chai').expect;

const stripe = testUtils.getSpyableStripe();

const v2EventPayloadWithoutRelatedObject = `
{
"context": "context",
"created": "1970-01-12T21:42:34.472Z",
"id": "obj_123",
"livemode": true,
"object":"v2.core.event",
"reason":
{
"type": "request",
"request":
{
"id": "obj_123",
"idempotency_key": "idempotency_key"
}
},
"type": "type"
}
`;

const v2EventPayloadWithRelatedObject = `
{
"context": "context",
"created": "1970-01-12T21:42:34.472Z",
"id": "obj_123",
"livemode": true,
"object":"v2.core.event",
"reason":
{
"type": "request",
"request":
{
"id": "obj_123",
"idempotency_key": "idempotency_key"
}
},
"type": "type",
"related_object":
{
"id": "obj_123",
"type": "thing",
"url": "/v1/things/obj_123"
}
}
`;

describe('V2 Core Events Resource', () => {
describe('retrieve', () => {
it('Sends the correct request', () => {
stripe.v2.core.events.retrieve('eventIdBaz');
expect(stripe.LAST_REQUEST).to.deep.equal({
method: 'GET',
url: '/v2/core/events/eventIdBaz',
headers: {},
data: null,
settings: {},
});
});

it('Does not have fetchRelatedObject if not needed', async () => {
const mockStripe = testUtils.createMockClient([
{
method: 'GET',
path: '/v2/core/events/ll_123',
response: v2EventPayloadWithoutRelatedObject,
},
]);
const event = await mockStripe.v2.core.events.retrieve('ll_123');
expect(event).ok;
expect(event.fetchRelatedObject).to.be.undefined;
});

it('Has fetchRelatedObject if needed', async () => {
const mockStripe = testUtils.createMockClient([
{
method: 'GET',
path: '/v2/core/events/ll_123',
response: v2EventPayloadWithRelatedObject,
},
]);
const event = await mockStripe.v2.core.events.retrieve('ll_123');
expect(event).ok;
expect(event.fetchRelatedObject).ok;
});

it('Can call fetchRelatedObject', async () => {
const mockStripe = testUtils.createMockClient([
{
method: 'GET',
path: '/v2/core/events/ll_123',
response: v2EventPayloadWithRelatedObject,
},
{
method: 'GET',
path: '/v1/things/obj_123',
response: '{"id": "obj_123"}',
},
]);
const event = await mockStripe.v2.core.events.retrieve('ll_123');
expect(event).ok;
expect(event.fetchRelatedObject).ok;
const obj = await event.fetchRelatedObject();
expect(obj.id).to.equal('obj_123');
});
});

describe('list', () => {
it('Sends the correct request', () => {
stripe.v2.core.events.list({object_id: 'foo'});
expect(stripe.LAST_REQUEST).to.deep.equal({
method: 'GET',
url: '/v2/core/events?object_id=foo',
headers: {},
data: null,
settings: {},
});
});

it('Does not have fetchRelatedObject if not needed', async () => {
const mockStripe = testUtils.createMockClient([
{
method: 'GET',
path: '/v2/core/events?object_id=foo',
response: `{
"data": [
${v2EventPayloadWithoutRelatedObject},
${v2EventPayloadWithoutRelatedObject},
${v2EventPayloadWithoutRelatedObject}
],
"next_page_url": null
}`,
},
]);
const resp = await mockStripe.v2.core.events.list({object_id: 'foo'});
expect(resp).ok;
expect(resp.data.length).is.equal(3);
for (const event of resp.data) {
expect(event.fetchRelatedObject).not.ok;
}
});

it('Has fetchRelatedObject if needed', async () => {
const mockStripe = testUtils.createMockClient([
{
method: 'GET',
path: '/v2/core/events?object_id=foo',
response: `{
"data": [
${v2EventPayloadWithRelatedObject},
${v2EventPayloadWithRelatedObject},
${v2EventPayloadWithRelatedObject}
],
"next_page_url": null
}`,
},
]);
const resp = await mockStripe.v2.core.events.list({object_id: 'foo'});
expect(resp).ok;
expect(resp.data.length).is.equal(3);
for (const event of resp.data) {
expect(event.fetchRelatedObject).ok;
}
});

it('Has fetchRelatedObject added to autoPaginate results', async () => {
const mockStripe = testUtils.createMockClient([
{
method: 'GET',
path: '/v2/core/events?object_id=foo',
response: `{
"data": [
${v2EventPayloadWithRelatedObject},
${v2EventPayloadWithRelatedObject},
${v2EventPayloadWithRelatedObject}
],
"next_page_url": "/next_page"
}`,
},
{
method: 'GET',
path: '/next_page',
response: `{
"data": [
${v2EventPayloadWithRelatedObject},
${v2EventPayloadWithRelatedObject},
${v2EventPayloadWithRelatedObject}
],
"next_page_url": null
}`,
},
]);
const respProm = mockStripe.v2.core.events.list({object_id: 'foo'});
expect(respProm).ok;
let totalEvents = 0;
await respProm.autoPagingEach(function(event) {
totalEvents += 1;
expect(event.fetchRelatedObject).ok;
});
expect(totalEvents).is.equal(6);
});
});
});
2 changes: 1 addition & 1 deletion test/testUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ export const createMockClient = (
throw new Error(`Unable to find a mock request for ${method} ${path}`);
}

callback(null, Promise.resolve(JSON.parse(request.response)));
callback(null, JSON.parse(request.response));
});
};

Expand Down
4 changes: 3 additions & 1 deletion types/V2/EventTypes.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,10 @@ declare module 'stripe' {
type: 'v1.billing.meter.error_report_triggered';
// Retrieves data specific to this event.
data: V1BillingMeterErrorReportTriggeredEvent.Data;
// Retrieves the object associated with the event.
// Object containing the reference to API resource relevant to the event.
related_object: Event.RelatedObject;
// Retrieves the object associated with the event.
fetchRelatedObject(): Promise<Billing.Meter>;
}

namespace V1BillingMeterErrorReportTriggeredEvent {
Expand Down
Loading