This reverts commit e02c8aa884.
This commit is contained in:
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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',
|
||||
})
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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',
|
||||
})
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 = `<h3>How to get the tierId</h3>
|
||||
<p>According to <a target="_blank" href="https://developer.opencollective.com/#/api/collectives?id=get-members-per-tier">open collectives documentation</a>, 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)</p>`
|
||||
|
||||
// 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 = [
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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',
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user