mirror of
https://github.com/discourse/discourse.git
synced 2026-08-14 13:58:53 +08:00
## Summary
The Patreon plugin maintains a synthetic `rewards[\"0\"]` tier ("All
Patrons") that catches every patron, including paying patrons whose
pledge doesn't map to any predefined tier. It's supposed to appear as
`\$0 - All Patrons` in the admin dropdown where group filters are
configured.
In affected campaigns, this option silently disappeared and was replaced
by `\$0 - Free` (Patreon's real default free tier, with its own numeric
id). Admins lost the ability to filter on "any patron".
### Root cause
`rewards[\"0\"] ||= {}` in `lib/campaign.rb` relied on Patreon's payload
having already populated `rewards[\"0\"][\"id\"] = \"0\"` via the
adapter's `parse_campaigns`. For campaigns where Patreon no longer
returns a reward keyed on `\"0\"` (e.g. when the campaign has a real
"Free" tier), the pseudo-tier ended up stored with only `title` and
`amount_cents` — no `id`.
The admin dropdown filters rewards client-side with `r.id >= 0` (in
`assets/javascripts/discourse/controllers/admin-plugins/patreon.js`).
Since `undefined >= 0` evaluates to `false`, "All Patrons" was dropped
from the options. Existing filter rules keyed on `\"0\"` continued to
work on the backend, but admins could no longer create or edit them.
### Fix
Assign `rewards[\"0\"][\"id\"] = \"0\"` explicitly so the pseudo-tier is
always selectable. Confirmed against a live v1 site:
`reward-users[\"0\"]` still tracked all 946 patrons, the backend state
was healthy, only the UI was broken. Applies equally to v1 and v2 since
the assignment runs adapter-agnostic.
Existing filter rules keyed on `\"0\"` keep working and start matching
again on the next sync — no data migration needed.
## Test plan
- [x] `bin/rspec plugins/discourse-patreon/spec/lib/campaign_spec.rb`
passes under both v1 and v2 shared examples, asserting `rewards[\"0\"]`
includes `id: \"0\"`
- [x] `bin/lint` clean
- [ ] On the affected site, run \`Patreon::Campaign.update!\` (or click
*Update Data*) and verify \`\$0 - All Patrons\` reappears in the admin
dropdown and can be assigned to a group
82 lines
3.1 KiB
Ruby
Vendored
82 lines
3.1 KiB
Ruby
Vendored
# frozen_string_literal: true
|
|
|
|
require_relative "../spec_helper"
|
|
|
|
RSpec.describe Patreon::Campaign do
|
|
shared_examples "campaign sync" do
|
|
it "should update campaigns and group users data" do
|
|
expect { described_class.update! }.to change { Group.count }.by(1)
|
|
|
|
expect(Group.find_by(name: "patrons")).to be_present
|
|
expect(Badge.find_by(name: "Patron")).to be_present
|
|
expect(Patreon.get("pledges").count).to eq(3)
|
|
expect(Patreon::Pledge::Decline.all.count).to eq(2)
|
|
expect(Patreon.get("rewards").count).to eq(expected_rewards_count)
|
|
expect(Patreon.get("rewards")["0"]).to include(
|
|
"id" => "0",
|
|
"title" => "All Patrons",
|
|
"amount_cents" => 0,
|
|
)
|
|
expect(Patreon.get("users").count).to eq(3)
|
|
expect(Patreon.get("reward-users")["0"].count).to eq(3)
|
|
expect(Patreon.get("filters").count).to eq(1)
|
|
|
|
expect {
|
|
Patreon
|
|
.get("users")
|
|
.each do |id, email|
|
|
cf = Fabricate(:user, email: email).custom_fields
|
|
expect(cf["patreon_id"]).to eq(id)
|
|
end
|
|
}.to change { GroupUser.count }.by(3)
|
|
end
|
|
end
|
|
|
|
context "with API v1" do
|
|
let(:expected_rewards_count) { 5 }
|
|
|
|
before do
|
|
campaigns_url =
|
|
"https://api.patreon.com/oauth2/api/current_user/campaigns?include=rewards,creator,goals,pledges&page%5Bcount%5D=100"
|
|
pledges_url =
|
|
"https://www.patreon.com/api/oauth2/api/campaigns/70261/pledges?page%5Bcount%5D=100&sort=created"
|
|
content = { status: 200, headers: { "Content-Type" => "application/json" } }
|
|
|
|
stub_request(:get, campaigns_url).to_return(
|
|
content.merge(body: get_patreon_response("v1/campaigns.json")),
|
|
)
|
|
stub_request(:get, pledges_url).to_return(
|
|
content.merge(body: get_patreon_response("v1/pledges.json")),
|
|
)
|
|
SiteSetting.patreon_enabled = true
|
|
SiteSetting.patreon_api_version = "1"
|
|
SiteSetting.patreon_declined_pledges_grace_period_days = 7
|
|
end
|
|
|
|
include_examples "campaign sync"
|
|
end
|
|
|
|
context "with API v2" do
|
|
let(:expected_rewards_count) { 4 }
|
|
|
|
before do
|
|
campaigns_url =
|
|
"https://www.patreon.com/api/oauth2/v2/campaigns?include=tiers,creator&fields%5Bcampaign%5D=created_at,name,patron_count&fields%5Btier%5D=title,amount_cents,created_at"
|
|
members_url =
|
|
"https://www.patreon.com/api/oauth2/v2/campaigns/0000000/members?include=currently_entitled_tiers,user&fields%5Bmember%5D=full_name,last_charge_date,last_charge_status,currently_entitled_amount_cents,patron_status,email&fields%5Buser%5D=email,full_name&fields%5Btier%5D=title,amount_cents,created_at&page%5Bcount%5D=1000"
|
|
content = { status: 200, headers: { "Content-Type" => "application/json" } }
|
|
|
|
stub_request(:get, campaigns_url).to_return(
|
|
content.merge(body: get_patreon_response("campaigns.json")),
|
|
)
|
|
stub_request(:get, members_url).to_return(
|
|
content.merge(body: get_patreon_response("members.json")),
|
|
)
|
|
SiteSetting.patreon_enabled = true
|
|
SiteSetting.patreon_api_version = "2"
|
|
SiteSetting.patreon_declined_pledges_grace_period_days = 7
|
|
end
|
|
|
|
include_examples "campaign sync"
|
|
end
|
|
end
|