From fb794ec479fb1c5af44b25e99ba1177e6ed39cd7 Mon Sep 17 00:00:00 2001 From: chris48s Date: Sat, 8 Jul 2023 13:13:06 +0100 Subject: [PATCH] Revert "[OpenCollective] update opencollective to api v2 (#9346)" (#9362) This reverts commit e02c8aa88434a5a8ae9eef8235738a62ef33cdb1. --- .../opencollective-all.service.js | 6 +- .../opencollective-all.tester.js | 36 +++++- .../opencollective-backers.service.js | 8 +- .../opencollective-backers.tester.js | 78 ++++++++++++- .../opencollective/opencollective-base.js | 109 +++++++++--------- .../opencollective-by-tier.service.js | 86 +------------- .../opencollective-sponsors.service.js | 7 +- .../opencollective-sponsors.tester.js | 70 ++++++++++- 8 files changed, 238 insertions(+), 162 deletions(-) diff --git a/services/opencollective/opencollective-all.service.js b/services/opencollective/opencollective-all.service.js index b7784bf36b4db..dac3229f97791 100644 --- a/services/opencollective/opencollective-all.service.js +++ b/services/opencollective/opencollective-all.service.js @@ -17,11 +17,7 @@ export default class OpencollectiveAll extends OpencollectiveBase { } async handle({ collective }) { - const data = await this.fetchCollectiveInfo({ - collective, - accountType: [], - }) - const backersCount = this.getCount(data) + const { backersCount } = await this.fetchCollectiveInfo(collective) return this.constructor.render(backersCount) } } diff --git a/services/opencollective/opencollective-all.tester.js b/services/opencollective/opencollective-all.tester.js index 61f2ef3e2cabb..d827313c509ad 100644 --- a/services/opencollective/opencollective-all.tester.js +++ b/services/opencollective/opencollective-all.tester.js @@ -2,6 +2,25 @@ import { nonNegativeInteger } from '../validators.js' import { createServiceTester } from '../tester.js' export const t = await createServiceTester() +t.create('renders correctly') + .get('/shields.json') + .intercept(nock => + nock('https://opencollective.com/').get('/shields.json').reply(200, { + slug: 'shields', + currency: 'USD', + image: + 'https://opencollective-production.s3-us-west-1.amazonaws.com/44dcbb90-1ee9-11e8-a4c3-7bb1885c0b6e.png', + balance: 105494, + yearlyIncome: 157371, + backersCount: 35, + contributorsCount: 276, + }) + ) + .expectBadge({ + label: 'backers and sponsors', + message: '35', + color: 'brightgreen', + }) t.create('gets amount of backers and sponsors') .get('/shields.json') .expectBadge({ @@ -9,10 +28,23 @@ t.create('gets amount of backers and sponsors') message: nonNegativeInteger, }) +t.create('renders not found correctly') + .get('/nonexistent-collective.json') + .intercept(nock => + nock('https://opencollective.com/') + .get('/nonexistent-collective.json') + .reply(404, 'Not found') + ) + .expectBadge({ + label: 'backers and sponsors', + message: 'collective not found', + color: 'red', + }) + t.create('handles not found correctly') .get('/nonexistent-collective.json') .expectBadge({ label: 'backers and sponsors', - message: 'No collective found with slug nonexistent-collective', - color: 'lightgrey', + message: 'collective not found', + color: 'red', }) diff --git a/services/opencollective/opencollective-backers.service.js b/services/opencollective/opencollective-backers.service.js index 8afdd56784e39..3f2b7da4d7541 100644 --- a/services/opencollective/opencollective-backers.service.js +++ b/services/opencollective/opencollective-backers.service.js @@ -17,12 +17,10 @@ export default class OpencollectiveBackers extends OpencollectiveBase { } async handle({ collective }) { - const data = await this.fetchCollectiveInfo({ + const { backersCount } = await this.fetchCollectiveBackersCount( collective, - accountType: ['INDIVIDUAL'], - }) - const backersCount = this.getCount(data) - + { userType: 'users' } + ) return this.constructor.render(backersCount) } } diff --git a/services/opencollective/opencollective-backers.tester.js b/services/opencollective/opencollective-backers.tester.js index 29855ae57d07c..7163f9a5da4ce 100644 --- a/services/opencollective/opencollective-backers.tester.js +++ b/services/opencollective/opencollective-backers.tester.js @@ -2,6 +2,80 @@ import { nonNegativeInteger } from '../validators.js' import { createServiceTester } from '../tester.js' export const t = await createServiceTester() +t.create('renders correctly') + .get('/shields.json') + .intercept(nock => + nock('https://opencollective.com/') + .get('/shields/members/users.json') + .reply(200, [ + { MemberId: 8685, type: 'USER', role: 'ADMIN' }, + { MemberId: 8686, type: 'USER', role: 'ADMIN' }, + { MemberId: 8682, type: 'USER', role: 'ADMIN' }, + { MemberId: 10305, type: 'USER', role: 'BACKER', tier: 'backer' }, + { MemberId: 10396, type: 'USER', role: 'BACKER', tier: 'backer' }, + { MemberId: 10733, type: 'USER', role: 'BACKER' }, + { MemberId: 8684, type: 'USER', role: 'ADMIN' }, + { MemberId: 10741, type: 'USER', role: 'BACKER' }, + { + MemberId: 10756, + type: 'USER', + role: 'BACKER', + tier: 'monthly backer', + }, + { MemberId: 11578, type: 'USER', role: 'CONTRIBUTOR' }, + { MemberId: 13459, type: 'USER', role: 'CONTRIBUTOR' }, + { + MemberId: 13507, + type: 'USER', + role: 'BACKER', + tier: 'monthly backer', + }, + { MemberId: 13512, type: 'USER', role: 'BACKER' }, + { MemberId: 13513, type: 'USER', role: 'FUNDRAISER' }, + { MemberId: 13984, type: 'USER', role: 'BACKER', tier: 'backer' }, + { MemberId: 14916, type: 'USER', role: 'BACKER' }, + { + MemberId: 16326, + type: 'USER', + role: 'BACKER', + tier: 'monthly backer', + }, + { MemberId: 18252, type: 'USER', role: 'BACKER', tier: 'backer' }, + { MemberId: 17631, type: 'USER', role: 'BACKER', tier: 'backer' }, + { + MemberId: 16420, + type: 'USER', + role: 'BACKER', + tier: 'monthly backer', + }, + { MemberId: 17186, type: 'USER', role: 'BACKER', tier: 'backer' }, + { MemberId: 18791, type: 'USER', role: 'BACKER', tier: 'backer' }, + { + MemberId: 19279, + type: 'USER', + role: 'BACKER', + tier: 'monthly backer', + }, + { MemberId: 19863, type: 'USER', role: 'BACKER', tier: 'backer' }, + { MemberId: 21451, type: 'USER', role: 'BACKER', tier: 'backer' }, + { MemberId: 22718, type: 'USER', role: 'BACKER' }, + { MemberId: 23561, type: 'USER', role: 'BACKER', tier: 'backer' }, + { MemberId: 25092, type: 'USER', role: 'CONTRIBUTOR' }, + { MemberId: 24473, type: 'USER', role: 'BACKER', tier: 'backer' }, + { MemberId: 25439, type: 'USER', role: 'BACKER', tier: 'backer' }, + { MemberId: 24483, type: 'USER', role: 'BACKER', tier: 'backer' }, + { MemberId: 25090, type: 'USER', role: 'CONTRIBUTOR' }, + { MemberId: 26404, type: 'USER', role: 'BACKER', tier: 'backer' }, + { MemberId: 27026, type: 'USER', role: 'BACKER', tier: 'backer' }, + { MemberId: 27132, type: 'USER', role: 'CONTRIBUTOR' }, + ]) + ) + .expectBadge({ + label: 'backers', + message: '25', + color: 'brightgreen', + }) + t.create('gets amount of backers').get('/shields.json').expectBadge({ label: 'backers', message: nonNegativeInteger, @@ -11,6 +85,6 @@ t.create('handles not found correctly') .get('/nonexistent-collective.json') .expectBadge({ label: 'backers', - message: 'No collective found with slug nonexistent-collective', - color: 'lightgrey', + message: 'collective not found', + color: 'red', }) diff --git a/services/opencollective/opencollective-base.js b/services/opencollective/opencollective-base.js index 2b4cc50340506..203cfabf03274 100644 --- a/services/opencollective/opencollective-base.js +++ b/services/opencollective/opencollective-base.js @@ -1,30 +1,26 @@ -import gql from 'graphql-tag' import Joi from 'joi' -import { BaseGraphqlService } from '../index.js' import { nonNegativeInteger } from '../validators.js' +import { BaseJsonService } from '../index.js' import { metric } from '../text-formatters.js' -const schema = Joi.object({ - data: Joi.object({ - account: Joi.object({ - name: Joi.string(), - slug: Joi.string(), - members: Joi.object({ - totalCount: nonNegativeInteger, - nodes: Joi.array().items( - Joi.object({ - tier: Joi.object({ - legacyId: Joi.number(), - name: Joi.string(), - }).allow(null), - }) - ), - }).required(), - }).required(), - }).required(), -}).required() +// https://developer.opencollective.com/#/api/collectives?id=get-info +const collectiveDetailsSchema = Joi.object().keys({ + slug: Joi.string().required(), + backersCount: nonNegativeInteger, +}) -export default class OpencollectiveBase extends BaseGraphqlService { +// https://developer.opencollective.com/#/api/collectives?id=get-members +function buildMembersArraySchema({ userType, tierRequired }) { + const keys = { + MemberId: Joi.number().required(), + type: userType || Joi.string().required(), + role: Joi.string().required(), + } + if (tierRequired) keys.tier = Joi.string().required() + return Joi.array().items(Joi.object().keys(keys)) +} + +export default class OpencollectiveBase extends BaseJsonService { static category = 'funding' static buildRoute(base, withTierId) { @@ -42,46 +38,45 @@ export default class OpencollectiveBase extends BaseGraphqlService { } } - async fetchCollectiveInfo({ collective, accountType }) { - return this._requestGraphql({ - schema, - url: 'https://api.opencollective.com/graphql/v2', - query: gql` - query account($slug: String, $accountType: [AccountType]) { - account(slug: $slug) { - name - slug - members(accountType: $accountType, role: BACKER) { - totalCount - nodes { - tier { - legacyId - name - } - } - } - } - } - `, - variables: { - slug: collective, - accountType, - }, - options: { - headers: { 'content-type': 'application/json' }, + async fetchCollectiveInfo(collective) { + return this._requestJson({ + schema: collectiveDetailsSchema, + // https://developer.opencollective.com/#/api/collectives?id=get-info + url: `https://opencollective.com/${collective}.json`, + httpErrors: { + 404: 'collective not found', }, }) } - getCount(data) { - const { - data: { - account: { - members: { totalCount }, - }, + async fetchCollectiveBackersCount(collective, { userType, tierId }) { + const schema = buildMembersArraySchema({ + userType: + userType === 'users' + ? 'USER' + : userType === 'organizations' + ? 'ORGANIZATION' + : undefined, + tierRequired: tierId, + }) + const members = await this._requestJson({ + schema, + // https://developer.opencollective.com/#/api/collectives?id=get-members + // https://developer.opencollective.com/#/api/collectives?id=get-members-per-tier + url: `https://opencollective.com/${collective}/members/${ + userType || 'all' + }.json${tierId ? `?TierId=${tierId}` : ''}`, + httpErrors: { + 404: 'collective not found', }, - } = data + }) - return totalCount + const result = { + backersCount: members.filter(member => member.role === 'BACKER').length, + } + // Find the title of the tier + if (tierId && members.length > 0) + result.tier = members.map(member => member.tier)[0] + return result } } diff --git a/services/opencollective/opencollective-by-tier.service.js b/services/opencollective/opencollective-by-tier.service.js index 673597df41d2f..98acc383f509d 100644 --- a/services/opencollective/opencollective-by-tier.service.js +++ b/services/opencollective/opencollective-by-tier.service.js @@ -1,91 +1,9 @@ -import Joi from 'joi' -import { nonNegativeInteger } from '../validators.js' -import { BaseJsonService } from '../index.js' -import { metric } from '../text-formatters.js' +import OpencollectiveBase from './opencollective-base.js' const documentation = `

How to get the tierId

According to open collectives documentation, you can find the tierId by looking at the URL after clicking on a Tier Card on the collective page. (e.g. tierId for https://opencollective.com/shields/order/2988 is 2988)

` -// https://developer.opencollective.com/#/api/collectives?id=get-info -const collectiveDetailsSchema = Joi.object().keys({ - slug: Joi.string().required(), - backersCount: nonNegativeInteger, -}) - -// https://developer.opencollective.com/#/api/collectives?id=get-members -function buildMembersArraySchema({ userType, tierRequired }) { - const keys = { - MemberId: Joi.number().required(), - type: userType || Joi.string().required(), - role: Joi.string().required(), - } - if (tierRequired) keys.tier = Joi.string().required() - return Joi.array().items(Joi.object().keys(keys)) -} - -class OpencollectiveBaseJson extends BaseJsonService { - static category = 'funding' - - static buildRoute(base, withTierId) { - return { - base: `opencollective${base ? `/${base}` : ''}`, - pattern: `:collective${withTierId ? '/:tierId' : ''}`, - } - } - - static render(backersCount, label) { - return { - label, - message: metric(backersCount), - color: backersCount > 0 ? 'brightgreen' : 'lightgrey', - } - } - - async fetchCollectiveInfo(collective) { - return this._requestJson({ - schema: collectiveDetailsSchema, - // https://developer.opencollective.com/#/api/collectives?id=get-info - url: `https://opencollective.com/${collective}.json`, - httpErrors: { - 404: 'collective not found', - }, - }) - } - - async fetchCollectiveBackersCount(collective, { userType, tierId }) { - const schema = buildMembersArraySchema({ - userType: - userType === 'users' - ? 'USER' - : userType === 'organizations' - ? 'ORGANIZATION' - : undefined, - tierRequired: tierId, - }) - const members = await this._requestJson({ - schema, - // https://developer.opencollective.com/#/api/collectives?id=get-members - // https://developer.opencollective.com/#/api/collectives?id=get-members-per-tier - url: `https://opencollective.com/${collective}/members/${ - userType || 'all' - }.json${tierId ? `?TierId=${tierId}` : ''}`, - httpErrors: { - 404: 'collective not found', - }, - }) - - const result = { - backersCount: members.filter(member => member.role === 'BACKER').length, - } - // Find the title of the tier - if (tierId && members.length > 0) - result.tier = members.map(member => member.tier)[0] - return result - } -} - -// TODO: 1. pagination is needed. 2. use new graphql api instead of legacy rest api -export default class OpencollectiveByTier extends OpencollectiveBaseJson { +export default class OpencollectiveByTier extends OpencollectiveBase { static route = this.buildRoute('tier', true) static examples = [ diff --git a/services/opencollective/opencollective-sponsors.service.js b/services/opencollective/opencollective-sponsors.service.js index c6015ce40db5e..0f41df6f03657 100644 --- a/services/opencollective/opencollective-sponsors.service.js +++ b/services/opencollective/opencollective-sponsors.service.js @@ -17,11 +17,10 @@ export default class OpencollectiveSponsors extends OpencollectiveBase { } async handle({ collective }) { - const data = await this.fetchCollectiveInfo({ + const { backersCount } = await this.fetchCollectiveBackersCount( collective, - accountType: ['ORGANIZATION'], - }) - const backersCount = this.getCount(data) + { userType: 'organizations' } + ) return this.constructor.render(backersCount) } } diff --git a/services/opencollective/opencollective-sponsors.tester.js b/services/opencollective/opencollective-sponsors.tester.js index d563744a49ae3..300af584c42ce 100644 --- a/services/opencollective/opencollective-sponsors.tester.js +++ b/services/opencollective/opencollective-sponsors.tester.js @@ -2,16 +2,80 @@ import { nonNegativeInteger } from '../validators.js' import { createServiceTester } from '../tester.js' export const t = await createServiceTester() +t.create('renders correctly') + .get('/shields.json') + .intercept(nock => + nock('https://opencollective.com/') + .get('/shields/members/organizations.json') + .reply(200, [ + { MemberId: 8683, type: 'ORGANIZATION', role: 'HOST' }, + { + MemberId: 13484, + type: 'ORGANIZATION', + role: 'BACKER', + tier: 'backer', + }, + { MemberId: 13508, type: 'ORGANIZATION', role: 'FUNDRAISER' }, + { MemberId: 15987, type: 'ORGANIZATION', role: 'BACKER' }, + { + MemberId: 16561, + type: 'ORGANIZATION', + role: 'BACKER', + tier: 'sponsor', + }, + { + MemberId: 16469, + type: 'ORGANIZATION', + role: 'BACKER', + tier: 'sponsor', + }, + { + MemberId: 18162, + type: 'ORGANIZATION', + role: 'BACKER', + tier: 'sponsor', + }, + { + MemberId: 21023, + type: 'ORGANIZATION', + role: 'BACKER', + tier: 'sponsor', + }, + { + MemberId: 21482, + type: 'ORGANIZATION', + role: 'BACKER', + tier: 'monthly backer', + }, + { + MemberId: 26367, + type: 'ORGANIZATION', + role: 'BACKER', + tier: 'monthly backer', + }, + { MemberId: 27531, type: 'ORGANIZATION', role: 'BACKER' }, + { + MemberId: 29443, + type: 'ORGANIZATION', + role: 'BACKER', + tier: 'monthly backer', + }, + ]) + ) + .expectBadge({ + label: 'sponsors', + message: '10', + color: 'brightgreen', + }) t.create('gets amount of sponsors').get('/shields.json').expectBadge({ label: 'sponsors', message: nonNegativeInteger, - color: 'brightgreen', }) t.create('handles not found correctly') .get('/nonexistent-collective.json') .expectBadge({ label: 'sponsors', - message: 'No collective found with slug nonexistent-collective', - color: 'lightgrey', + message: 'collective not found', + color: 'red', })