fix(app-store): handle reused flag and response pagination
This commit is contained in:
@@ -18,8 +18,6 @@ class AppStoreConnect::Client
|
||||
fresh_payloads = fresh_review_payloads(review_payloads, since)
|
||||
reviews.concat(fresh_payloads)
|
||||
|
||||
break if since.present? && fresh_payloads.size < review_payloads.size
|
||||
|
||||
next_url = payload.dig('links', 'next')
|
||||
break if next_url.blank?
|
||||
end
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
class RepurposeMessageReplyToFlagForChannelAppStore < ActiveRecord::Migration[7.1]
|
||||
def up
|
||||
# The message_reply_to flag (deprecated) has been renamed to channel_app_store.
|
||||
# Disable it on any accounts that had message_reply_to enabled so the repurposed
|
||||
# flag starts from plan/default configuration instead of inheriting stale state.
|
||||
Account.feature_channel_app_store.find_each(batch_size: 100) do |account|
|
||||
account.disable_features(:channel_app_store)
|
||||
account.save!(validate: false)
|
||||
end
|
||||
|
||||
# Remove the stale message_reply_to entry from ACCOUNT_LEVEL_FEATURE_DEFAULTS.
|
||||
# ConfigLoader only adds new flags; it never removes renamed ones.
|
||||
config = InstallationConfig.find_by(name: 'ACCOUNT_LEVEL_FEATURE_DEFAULTS')
|
||||
return if config&.value.blank?
|
||||
|
||||
config.value = config.value.reject { |feature| feature['name'] == 'message_reply_to' }
|
||||
config.save!
|
||||
GlobalConfig.clear_cache
|
||||
end
|
||||
end
|
||||
@@ -75,39 +75,17 @@ RSpec.describe AppStoreConnect::Client do
|
||||
expect(review_payload['response']['id']).to eq('response-1')
|
||||
end
|
||||
|
||||
it 'stops fetching when a scheduled sync reaches already synced reviews' do
|
||||
it 'keeps fetching older pages so updated developer responses are not skipped' do
|
||||
stub_request(:get, 'https://api.appstoreconnect.apple.com/v1/apps/123456789/customerReviews')
|
||||
.with(query: { include: 'response', limit: '200', sort: '-createdDate' })
|
||||
.to_return(
|
||||
status: 200,
|
||||
body: {
|
||||
data: [
|
||||
{
|
||||
id: 'review-1',
|
||||
type: 'customerReviews',
|
||||
attributes: {
|
||||
createdDate: '2026-05-20T10:00:00-00:00'
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'review-2',
|
||||
type: 'customerReviews',
|
||||
attributes: {
|
||||
createdDate: '2026-05-19T10:00:00-00:00'
|
||||
}
|
||||
}
|
||||
],
|
||||
links: {
|
||||
next: 'https://api.appstoreconnect.apple.com/v1/apps/123456789/customerReviews?page=2'
|
||||
}
|
||||
}.to_json,
|
||||
headers: { 'Content-Type' => 'application/json' }
|
||||
)
|
||||
.to_return(app_store_response(mixed_freshness_reviews_page))
|
||||
stub_request(:get, 'https://api.appstoreconnect.apple.com/v1/apps/123456789/customerReviews?page=2')
|
||||
.to_return(app_store_response(older_reviews_page_with_updated_response))
|
||||
|
||||
review_payloads = described_class.new(channel: channel).fetch_reviews(since: Time.zone.parse('2026-05-20T00:00:00-00:00'))
|
||||
|
||||
expect(review_payloads.pluck('review').pluck('id')).to eq(['review-1'])
|
||||
expect(WebMock).not_to have_requested(:get, 'https://api.appstoreconnect.apple.com/v1/apps/123456789/customerReviews?page=2')
|
||||
expect(review_payloads.pluck('review').pluck('id')).to eq(%w[review-1 review-3])
|
||||
expect(WebMock).to have_requested(:get, 'https://api.appstoreconnect.apple.com/v1/apps/123456789/customerReviews?page=2')
|
||||
end
|
||||
|
||||
it 'includes older reviews when the developer response was updated after the sync cursor' do
|
||||
@@ -187,6 +165,67 @@ RSpec.describe AppStoreConnect::Client do
|
||||
|
||||
expect(AppStoreConnect::TokenService).to have_received(:new).twice
|
||||
end
|
||||
|
||||
def app_store_response(body)
|
||||
{
|
||||
status: 200,
|
||||
body: body.to_json,
|
||||
headers: { 'Content-Type' => 'application/json' }
|
||||
}
|
||||
end
|
||||
|
||||
def mixed_freshness_reviews_page
|
||||
{
|
||||
data: [
|
||||
{
|
||||
id: 'review-1',
|
||||
type: 'customerReviews',
|
||||
attributes: { createdDate: '2026-05-20T10:00:00-00:00' }
|
||||
},
|
||||
{
|
||||
id: 'review-2',
|
||||
type: 'customerReviews',
|
||||
attributes: { createdDate: '2026-05-19T10:00:00-00:00' }
|
||||
}
|
||||
],
|
||||
links: {
|
||||
next: 'https://api.appstoreconnect.apple.com/v1/apps/123456789/customerReviews?page=2'
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def older_reviews_page_with_updated_response
|
||||
{
|
||||
data: [
|
||||
{
|
||||
id: 'review-3',
|
||||
type: 'customerReviews',
|
||||
attributes: { createdDate: '2026-05-18T10:00:00-00:00' },
|
||||
relationships: response_relationship('response-3')
|
||||
}
|
||||
],
|
||||
included: [updated_response_payload]
|
||||
}
|
||||
end
|
||||
|
||||
def response_relationship(response_id)
|
||||
{
|
||||
response: {
|
||||
data: { id: response_id, type: 'customerReviewResponses' }
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def updated_response_payload
|
||||
{
|
||||
id: 'response-3',
|
||||
type: 'customerReviewResponses',
|
||||
attributes: {
|
||||
responseBody: 'Updated response',
|
||||
lastModifiedDate: '2026-05-20T11:00:00-00:00'
|
||||
}
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
describe '#create_review_response' do
|
||||
|
||||
Reference in New Issue
Block a user