Skip to content
Open
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 lib/controllers/v2/users_controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ const show = async req => {
"journal_posts_count",
"last_active",
"login",
"monthly_supporter",
"monthly_supporter_badge",
"name",
"observations_count",
"orcid",
Expand Down
28 changes: 21 additions & 7 deletions lib/models/user.js
Original file line number Diff line number Diff line change
Expand Up @@ -94,13 +94,27 @@ const User = class User extends Model {
const query = squel.select( )
.from( User.tableName )
.where( "id = ?", id );
if ( dbFields.indexOf( "monthly_supporter" ) >= 0 ) {
_.pull( dbFields, "monthly_supporter" );
query.field(
"( donorbox_plan_type = 'monthly' AND donorbox_plan_status = 'active' ) OR "
+ "( fundraiseup_plan_frequency = 'monthly' AND fundraiseup_plan_status = 'active' )",
"monthly_supporter"
);
if ( dbFields.indexOf( "monthly_supporter_badge" ) >= 0 ) {
_.pull( dbFields, "monthly_supporter_badge" );
const prefsQuery = squel.select( )
.field( "value" )
.from( "preferences" )
.where( "owner_id = ? AND owner_type = 'User' AND name = 'prefers_monthly_supporter_badge'", id );
const { rows: results } = await pgClient.replica.query( prefsQuery.toString( ) );
const userWantsToShowBadge = _.isEmpty( results ) || _.isEmpty( results[0] )
? _.find( PREFS, { name: "monthly_supporter_badge" } ).default
: results[0].value === "t";
if ( userWantsToShowBadge ) {
// CASE clause ensures true/false return vals, else the expression can evaluate to null.
query.field(
"CASE WHEN (( donorbox_plan_type = 'monthly' AND donorbox_plan_status = 'active' ) OR "
+ "( fundraiseup_plan_frequency = 'monthly' AND fundraiseup_plan_status = 'active' )) "
+ "THEN true ELSE false END",
"monthly_supporter_badge"
);
} else {
query.field( "false", "monthly_supporter_badge" );
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you’d rather this case return null instead of false, let me know. I originally had it returning null previously

}
}
if ( fields.indexOf( "site" ) ) {
_.pull( dbFields, "site" );
Expand Down
1 change: 1 addition & 0 deletions openapi/schema/response/private_user.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ module.exports = user.append( {
email: Joi.string( ).valid( null ),
locale: Joi.string( ).valid( null ),
login: Joi.string( ),
monthly_supporter: Joi.boolean( ).valid( null ),
muted_user_ids: Joi.array( ).items(
Joi.number( ).integer( )
).valid( null ),
Expand Down
2 changes: 1 addition & 1 deletion openapi/schema/response/user.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ module.exports = Joi.object( ).keys( {
annotated_observations_count: Joi.number( ).integer( ),
last_active: Joi.date( ).iso( ),
login: Joi.string( ),
monthly_supporter: Joi.boolean( ).valid( null ),
monthly_supporter_badge: Joi.boolean( ).valid( null ),
name: Joi.string( ).valid( null ),
observations_count: Joi.number( ).integer( ),
orcid: Joi.string( ).valid( null ),
Expand Down
93 changes: 93 additions & 0 deletions schema/fixtures.js
Original file line number Diff line number Diff line change
Expand Up @@ -2025,6 +2025,38 @@
"email": "user2023092503@gmail.com",
"last_ip": "192.168.0.3",
"suspended": false
},
{
"id": 2025100708,
"login": "user2025100708",
"name": "User2025100708 is an active donorbox monthly donor but shy about it",
"email": "user2025100708@gmail.com",
"last_ip": "192.168.0.3",
"suspended": false
},
{
"id": 2025100709,
"login": "user2025100709",
"name": "User2025100709 is an inactive donorbox monthly donor and boastful about it",
"email": "user2025100709@gmail.com",
"last_ip": "192.168.0.3",
"suspended": false
},
{
"id": 2025100710,
"login": "user2025100710",
"name": "User2025100710 is an active fundraiseup monthly donor and boastful about it",
"email": "user2025100710@gmail.com",
"last_ip": "192.168.0.3",
"suspended": false
},
{
"id": 2025100711,
"login": "user2025100711",
"name": "User2025100711 is an active fundraiseup nonmonthly donor and boastful about it",
"email": "user2025100711@gmail.com",
"last_ip": "192.168.0.3",
"suspended": false
}
]
},
Expand Down Expand Up @@ -3068,6 +3100,27 @@
"owner_type": "Project",
"owner_id": 2005,
"value": "t"
},
{
"id": 2025100700,
"name": "prefers_monthly_supporter_badge",
"owner_id": 2025100709,
"owner_type": "User",
"value": "t"
},
{
"id": 2025100701,
"name": "prefers_monthly_supporter_badge",
"owner_id": 2025100710,
"owner_type": "User",
"value": "t"
},
{
"id": 2025100702,
"name": "prefers_monthly_supporter_badge",
"owner_id": 2025100711,
"owner_type": "User",
"value": "t"
}
],
"project_observations": [
Expand Down Expand Up @@ -3739,6 +3792,46 @@
"login": "user2024071702",
"name": "User2024071702 with 2023 donation",
"created_at": "2020-01-01 00:00:00"
},
{
"id": 2025100708,
"login": "user2025100708",
"name": "User2025100708 is an active donorbox monthly donor but shy about it",
"created_at": "2025-10-07 00:00:00",
"updated_at": "2025-10-07 00:00:00",
"last_active": "2025-10-07",
"donorbox_plan_type": "monthly",
"donorbox_plan_status": "active"
},
{
"id": 2025100709,
"login": "user2025100709",
"name": "User2025100709 is an inactive donorbox monthly donor and boastful about it",
"created_at": "2025-10-07 00:00:00",
"updated_at": "2025-10-07 00:00:00",
"last_active": "2025-10-07",
"donorbox_plan_type": "monthly",
"donorbox_plan_status": "inactive"
},
{
"id": 2025100710,
"login": "user2025100710",
"name": "User2025100710 is an active fundraiseup monthly donor and boastful about it",
"created_at": "2025-10-07 00:00:00",
"updated_at": "2025-10-07 00:00:00",
"last_active": "2025-10-07",
"fundraiseup_plan_frequency": "monthly",
"fundraiseup_plan_status": "active"
},
{
"id": 2025100711,
"login": "user2025100711",
"name": "User2025100711 is an active fundraiseup nonmonthly donor and boastful about it",
"created_at": "2025-10-07 00:00:00",
"updated_at": "2025-10-07 00:00:00",
"last_active": "2025-10-07",
"fundraiseup_plan_frequency": "yearly",
"fundraiseup_plan_status": "active"
}
],
"user_blocks": [
Expand Down
66 changes: 66 additions & 0 deletions test/integration/v2/users.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,72 @@ describe( "Users", ( ) => {
} ).expect( "Content-Type", /json/ )
.expect( 200, done );
} );

describe( "monthly_supporter_badge property", ( ) => {
it( "is false if user does not prefer to show it", function ( done ) {
request( this.app ).get( "/v2/users/2025100708?fields=all" )
.expect( res => {
const user = res.body.results[0];
expect( res.body.page ).to.eq( 1 );
expect( res.body.per_page ).to.eq( 1 );
expect( res.body.total_results ).to.eq( 1 );
expect( res.body.results.length ).to.eq( 1 );
expect( user.id ).to.eq( 2025100708 );
expect( user ).to.have.property( "monthly_supporter_badge" );
expect( user.monthly_supporter_badge ).to.be.false;
} )
.expect( "Content-Type", /json/ )
.expect( 200, done );
} );

it( "is false if user is an inactive donor", function ( done ) {
request( this.app ).get( "/v2/users/2025100709?fields=all" )
.expect( res => {
const user = res.body.results[0];
expect( res.body.page ).to.eq( 1 );
expect( res.body.per_page ).to.eq( 1 );
expect( res.body.total_results ).to.eq( 1 );
expect( res.body.results.length ).to.eq( 1 );
expect( user.id ).to.eq( 2025100709 );
expect( user ).to.have.property( "monthly_supporter_badge" );
expect( user.monthly_supporter_badge ).to.be.false;
} )
.expect( "Content-Type", /json/ )
.expect( 200, done );
} );

it( "is true if user is an active monthly donor and prefers to show it", function ( done ) {
request( this.app ).get( "/v2/users/2025100710?fields=all" )
.expect( res => {
const user = res.body.results[0];
expect( res.body.page ).to.eq( 1 );
expect( res.body.per_page ).to.eq( 1 );
expect( res.body.total_results ).to.eq( 1 );
expect( res.body.results.length ).to.eq( 1 );
expect( user.id ).to.eq( 2025100710 );
expect( user ).to.have.property( "monthly_supporter_badge" );
expect( user.monthly_supporter_badge ).to.be.true;
} )
.expect( "Content-Type", /json/ )
.expect( 200, done );
} );

it( "is false if user is an active yearly donor, not monthly", function ( done ) {
request( this.app ).get( "/v2/users/2025100711?fields=all" )
.expect( res => {
const user = res.body.results[0];
expect( res.body.page ).to.eq( 1 );
expect( res.body.per_page ).to.eq( 1 );
expect( res.body.total_results ).to.eq( 1 );
expect( res.body.results.length ).to.eq( 1 );
expect( user.id ).to.eq( 2025100711 );
expect( user ).to.have.property( "monthly_supporter_badge" );
expect( user.monthly_supporter_badge ).to.be.false;
} )
.expect( "Content-Type", /json/ )
.expect( 200, done );
} );
} );
} );

describe( "autocomplete", ( ) => {
Expand Down