diff --git a/app/lib/fake/backend/fake_auth_provider.dart b/app/lib/fake/backend/fake_auth_provider.dart index e47edd1c68..62c746e71a 100644 --- a/app/lib/fake/backend/fake_auth_provider.dart +++ b/app/lib/fake/backend/fake_auth_provider.dart @@ -416,15 +416,20 @@ Future _acquireCsrfToken({ /// Creates a pub.dev API client and executes [fn], making sure that the HTTP /// resources are freed after the callback finishes. +/// The callback [fn] is retried on the transient network errors. /// /// The [email] is used to create an HTTP session and the related CSRF token is /// extracted from the session, both are sent alongside the requests. -Future withFakeAuthHttpPubApiClient( +Future withFakeAuthRetryPubApiClient( Future Function(PubApiClient client) fn, { required String email, List? scopes, + + /// The base URL of the pub server. String? pubHostedUrl, - Set? experimental, + + /// The enabled experiments that will be part of the experimental cookie. + Set? experiments, }) async { final sessionId = await _acquireFakeSessionId( email: email, @@ -436,11 +441,11 @@ Future withFakeAuthHttpPubApiClient( pubHostedUrl: pubHostedUrl, ); - return await withHttpPubApiClient( + return await withRetryPubApiClient( sessionId: sessionId, csrfToken: csrfToken, pubHostedUrl: pubHostedUrl, - experimental: experimental, + experiments: experiments, fn, ); } diff --git a/app/lib/tool/test_profile/importer.dart b/app/lib/tool/test_profile/importer.dart index 7fbe711548..ef759f08e1 100644 --- a/app/lib/tool/test_profile/importer.dart +++ b/app/lib/tool/test_profile/importer.dart @@ -45,7 +45,7 @@ Future importProfile({ // create publishers for (final p in profile.publishers) { final firstMemberEmail = p.members.first.email; - await withFakeAuthHttpPubApiClient( + await withFakeAuthRetryPubApiClient( email: firstMemberEmail, scopes: [webmasterScope], pubHostedUrl: pubHostedUrl, @@ -94,8 +94,8 @@ Future importProfile({ await source.getArchiveBytes(rv.package, rv.version); bytes = await _mayCleanupTarModeBits(bytes); try { - await withHttpPubApiClient( - bearerToken: createFakeAuthTokenForEmail(uploaderEmail, + await withRetryPubApiClient( + authToken: createFakeAuthTokenForEmail(uploaderEmail, audience: activeConfiguration.pubClientAudience), pubHostedUrl: pubHostedUrl, (client) => client.uploadPackageBytes(bytes), @@ -120,7 +120,7 @@ Future importProfile({ final packageName = testPackage.name; final activeEmail = lastActiveUploaderEmails[packageName]; - await withFakeAuthHttpPubApiClient( + await withFakeAuthRetryPubApiClient( email: activeEmail!, pubHostedUrl: pubHostedUrl, (client) async { @@ -151,8 +151,8 @@ Future importProfile({ ); if (testPackage.isFlutterFavorite ?? false) { - await withHttpPubApiClient( - bearerToken: + await withRetryPubApiClient( + authToken: createFakeServiceAccountToken(email: adminUserEmail ?? activeEmail), pubHostedUrl: pubHostedUrl, (client) async { @@ -170,7 +170,7 @@ Future importProfile({ final createLikeCounts = {}; // create users for (final u in profile.users) { - await withFakeAuthHttpPubApiClient( + await withFakeAuthRetryPubApiClient( email: u.email, pubHostedUrl: pubHostedUrl, (client) async { @@ -194,7 +194,7 @@ Future importProfile({ for (var i = 0; i < likesMissing; i++) { final userEmail = 'like-$i@pub.dev'; - await withFakeAuthHttpPubApiClient( + await withFakeAuthRetryPubApiClient( email: userEmail, pubHostedUrl: pubHostedUrl, (client) async { diff --git a/app/lib/tool/utils/pub_api_client.dart b/app/lib/tool/utils/pub_api_client.dart index 8d013adb34..087cbd8627 100644 --- a/app/lib/tool/utils/pub_api_client.dart +++ b/app/lib/tool/utils/pub_api_client.dart @@ -96,22 +96,32 @@ class _FakeTimeClient implements http.Client { /// resources are freed after the callback finishes. /// The callback [fn] is retried on the transient network errors. /// -/// If [bearerToken], [sessionId] or [csrfToken] is specified, the corresponding +/// If [authToken], [sessionId] or [csrfToken] is specified, the corresponding /// HTTP header will be sent alongside the request. -Future withHttpPubApiClient( +Future withRetryPubApiClient( + /// The callback function that may be retried on transient errors. Future Function(PubApiClient client) fn, { - String? bearerToken, + /// The token to use as the `Authorization` header in the format of `Bearer `. + String? authToken, + + /// The session id that will be part of the session cookie. String? sessionId, + + /// The CSRF token that will be the value of the CSRF header (`x-pub-csrf-token`). String? csrfToken, + + /// The base URL of the pub server. String? pubHostedUrl, - Set? experimental, + + /// The enabled experiments that will be part of the experimental cookie. + Set? experiments, }) async { final httpClient = httpClientWithAuthorization( - tokenProvider: () async => bearerToken, + tokenProvider: () async => authToken, sessionIdProvider: () async => sessionId, csrfTokenProvider: () async => csrfToken, cookieProvider: () async => { - if (experimental != null) experimentalCookieName: experimental.join(':'), + if (experiments != null) experimentalCookieName: experiments.join(':'), }, client: http.Client(), ); diff --git a/app/test/account/consent_backend_test.dart b/app/test/account/consent_backend_test.dart index c764d9c9de..772461d70d 100644 --- a/app/test/account/consent_backend_test.dart +++ b/app/test/account/consent_backend_test.dart @@ -25,7 +25,7 @@ void main() { group('Uploader invite', () { Future inviteUploader( {String adminEmail = 'admin@pub.dev'}) async { - await withFakeAuthHttpPubApiClient( + await withFakeAuthRetryPubApiClient( email: adminEmail, pubHostedUrl: activeConfiguration.primarySiteUri.toString(), (client) async { @@ -309,7 +309,7 @@ void main() { group('Sanity check', () { testWithProfile('consent parameter length', fn: () async { - await withFakeAuthHttpPubApiClient(email: adminAtPubDevEmail, (c) async { + await withFakeAuthRetryPubApiClient(email: adminAtPubDevEmail, (c) async { await expectApiException( c.consentInfo('abcd' * 500), status: 400, diff --git a/app/test/admin/exported_api_sync_test.dart b/app/test/admin/exported_api_sync_test.dart index 82f7920d4c..cf27de267e 100644 --- a/app/test/admin/exported_api_sync_test.dart +++ b/app/test/admin/exported_api_sync_test.dart @@ -19,8 +19,8 @@ void main() { List? packages, bool forceWrite = false, }) async { - await withHttpPubApiClient( - bearerToken: siteAdminToken, + await withRetryPubApiClient( + authToken: siteAdminToken, (api) async { await api.adminInvokeAction( 'exported-api-sync', diff --git a/app/test/admin/moderate_package_test.dart b/app/test/admin/moderate_package_test.dart index 87d4a25a4f..f5377d4485 100644 --- a/app/test/admin/moderate_package_test.dart +++ b/app/test/admin/moderate_package_test.dart @@ -33,7 +33,7 @@ import '../shared/test_services.dart'; void main() { group('Moderate package', () { Future _report(String package) async { - await withHttpPubApiClient( + await withRetryPubApiClient( (client) async { await client.postReport(ReportForm( email: 'user@pub.dev', diff --git a/app/test/admin/moderate_package_version_test.dart b/app/test/admin/moderate_package_version_test.dart index ab4fbee971..3f32570d2a 100644 --- a/app/test/admin/moderate_package_version_test.dart +++ b/app/test/admin/moderate_package_version_test.dart @@ -33,7 +33,7 @@ import '../shared/test_services.dart'; void main() { group('Moderate package version', () { Future _report(String package, String version) async { - await withHttpPubApiClient( + await withRetryPubApiClient( (client) async { await client.postReport(ReportForm( email: 'user@pub.dev', diff --git a/app/test/admin/moderate_publisher_test.dart b/app/test/admin/moderate_publisher_test.dart index abb1075acf..dd8922a653 100644 --- a/app/test/admin/moderate_publisher_test.dart +++ b/app/test/admin/moderate_publisher_test.dart @@ -24,7 +24,7 @@ import '../shared/test_services.dart'; void main() { group('Moderate Publisher', () { Future _report(String publisherId) async { - await withHttpPubApiClient( + await withRetryPubApiClient( (client) async { await client.postReport(ReportForm( email: 'user@pub.dev', diff --git a/app/test/admin/moderate_user_test.dart b/app/test/admin/moderate_user_test.dart index f7e0e73dd5..246a318322 100644 --- a/app/test/admin/moderate_user_test.dart +++ b/app/test/admin/moderate_user_test.dart @@ -28,7 +28,7 @@ import '../shared/test_services.dart'; void main() { group('Moderate User', () { Future _report(String package) async { - await withHttpPubApiClient( + await withRetryPubApiClient( (client) async { await client.postReport(account_api.ReportForm( email: 'user@pub.dev', diff --git a/app/test/admin/moderation_case_resolve_test.dart b/app/test/admin/moderation_case_resolve_test.dart index 0b421572b6..64717dc48d 100644 --- a/app/test/admin/moderation_case_resolve_test.dart +++ b/app/test/admin/moderation_case_resolve_test.dart @@ -18,7 +18,7 @@ void main() { String? appealCaseId, required bool? apply, }) async { - await withHttpPubApiClient( + await withRetryPubApiClient( (client) async { await client.postReport(ReportForm( email: 'user@pub.dev', diff --git a/app/test/admin/moderation_transparency_metrics_test.dart b/app/test/admin/moderation_transparency_metrics_test.dart index 47c14f735b..59707ef4dc 100644 --- a/app/test/admin/moderation_transparency_metrics_test.dart +++ b/app/test/admin/moderation_transparency_metrics_test.dart @@ -21,7 +21,7 @@ void main() { String? email, String? caseId, }) async { - await withHttpPubApiClient( + await withRetryPubApiClient( (client) async { await client.postReport(account_api.ReportForm( email: email ?? 'user@pub.dev', diff --git a/app/test/frontend/handlers/report_test.dart b/app/test/frontend/handlers/report_test.dart index bd7fc4073c..d9152f44ab 100644 --- a/app/test/frontend/handlers/report_test.dart +++ b/app/test/frontend/handlers/report_test.dart @@ -78,7 +78,7 @@ void main() { group('Report API test', () { testWithProfile('unauthenticated email missing', fn: () async { - await withHttpPubApiClient( + await withRetryPubApiClient( (client) async { await expectApiException( client.postReport(ReportForm( @@ -97,7 +97,7 @@ void main() { await withFakeAuthRequestContext('user@pub.dev', () async { final sessionId = requestContext.sessionData?.sessionId; final csrfToken = requestContext.csrfToken; - await withHttpPubApiClient( + await withRetryPubApiClient( sessionId: sessionId, csrfToken: csrfToken, (client) async { @@ -117,7 +117,7 @@ void main() { }); testWithProfile('subject missing', fn: () async { - await withHttpPubApiClient( + await withRetryPubApiClient( (client) async { await expectApiException( client.postReport(ReportForm( @@ -134,7 +134,7 @@ void main() { }); testWithProfile('subject is invalid', fn: () async { - await withHttpPubApiClient( + await withRetryPubApiClient( (client) async { await expectApiException( client.postReport(ReportForm( @@ -152,7 +152,7 @@ void main() { }); testWithProfile('package missing', fn: () async { - await withHttpPubApiClient( + await withRetryPubApiClient( (client) async { await expectApiException( client.postReport(ReportForm( @@ -170,7 +170,7 @@ void main() { }); testWithProfile('version missing', fn: () async { - await withHttpPubApiClient( + await withRetryPubApiClient( (client) async { await expectApiException( client.postReport(ReportForm( @@ -188,7 +188,7 @@ void main() { }); testWithProfile('publisher missing', fn: () async { - await withHttpPubApiClient( + await withRetryPubApiClient( (client) async { await expectApiException( client.postReport(ReportForm( @@ -209,7 +209,7 @@ void main() { await withFakeAuthRequestContext('user@pub.dev', () async { final sessionId = requestContext.sessionData?.sessionId; final csrfToken = requestContext.csrfToken; - await withHttpPubApiClient( + await withRetryPubApiClient( sessionId: sessionId, csrfToken: csrfToken, (client) async { @@ -229,7 +229,7 @@ void main() { }); testWithProfile('unauthenticated report success', fn: () async { - await withHttpPubApiClient( + await withRetryPubApiClient( (client) async { final msg = await client.postReport(ReportForm( email: 'user2@pub.dev', @@ -256,7 +256,7 @@ void main() { await withFakeAuthRequestContext('user@pub.dev', () async { final sessionId = requestContext.sessionData?.sessionId; final csrfToken = requestContext.csrfToken; - await withHttpPubApiClient( + await withRetryPubApiClient( sessionId: sessionId, csrfToken: csrfToken, (client) async { @@ -307,7 +307,7 @@ void main() { } testWithProfile('failure: case does not exists', fn: () async { - await withHttpPubApiClient( + await withRetryPubApiClient( (client) async { await expectApiException( client.postReport(ReportForm( @@ -326,7 +326,7 @@ void main() { testWithProfile('failure: case is not closed', fn: () async { await _prepareApplied(status: ModerationStatus.pending); - await withHttpPubApiClient( + await withRetryPubApiClient( (client) async { await expectApiException( client.postReport(ReportForm( @@ -345,7 +345,7 @@ void main() { testWithProfile('failure: subject is not on the case', fn: () async { await _prepareApplied(); - await withHttpPubApiClient( + await withRetryPubApiClient( (client) async { await expectApiException( client.postReport(ReportForm( @@ -370,7 +370,7 @@ void main() { ); // first report: success - await withHttpPubApiClient( + await withRetryPubApiClient( (client) async { final msg = await client.postReport(ReportForm( email: 'user2@pub.dev', @@ -400,7 +400,7 @@ void main() { ); // second report: rejected - await withHttpPubApiClient((client) async { + await withRetryPubApiClient((client) async { await expectApiException( client.postReport(ReportForm( email: 'user2@pub.dev', @@ -421,7 +421,7 @@ void main() { logSubject: 'package-version:oxygen/1.2.0', ); - await withFakeAuthHttpPubApiClient( + await withFakeAuthRetryPubApiClient( email: 'admin@pub.dev', (client) async { final msg = await client.postReport(ReportForm( diff --git a/app/test/package/api_export/api_exporter_test.dart b/app/test/package/api_export/api_exporter_test.dart index a8a403da67..b817068860 100644 --- a/app/test/package/api_export/api_exporter_test.dart +++ b/app/test/package/api_export/api_exporter_test.dart @@ -293,8 +293,8 @@ Future _testExportedApiSynchronization( // recently created files as a guard against race conditions. fakeTime.elapseSync(days: 1); - await withHttpPubApiClient( - bearerToken: createFakeServiceAccountToken(email: 'admin@pub.dev'), + await withRetryPubApiClient( + authToken: createFakeServiceAccountToken(email: 'admin@pub.dev'), (adminApi) async { await adminApi.adminInvokeAction( 'moderate-package-version', @@ -332,8 +332,8 @@ Future _testExportedApiSynchronization( _log.info('## Version reinstated'); { - await withHttpPubApiClient( - bearerToken: createFakeServiceAccountToken(email: 'admin@pub.dev'), + await withRetryPubApiClient( + authToken: createFakeServiceAccountToken(email: 'admin@pub.dev'), (adminApi) async { await adminApi.adminInvokeAction( 'moderate-package-version', @@ -374,8 +374,8 @@ Future _testExportedApiSynchronization( // recently created files as a guard against race conditions. fakeTime.elapseSync(days: 1); - await withHttpPubApiClient( - bearerToken: createFakeServiceAccountToken(email: 'admin@pub.dev'), + await withRetryPubApiClient( + authToken: createFakeServiceAccountToken(email: 'admin@pub.dev'), (adminApi) async { await adminApi.adminInvokeAction( 'moderate-package', @@ -406,8 +406,8 @@ Future _testExportedApiSynchronization( _log.info('## Package reinstated'); { - await withHttpPubApiClient( - bearerToken: createFakeServiceAccountToken(email: 'admin@pub.dev'), + await withRetryPubApiClient( + authToken: createFakeServiceAccountToken(email: 'admin@pub.dev'), (adminApi) async { await adminApi.adminInvokeAction( 'moderate-package', diff --git a/app/test/package/upload_test.dart b/app/test/package/upload_test.dart index 7365bd0cc0..576862012d 100644 --- a/app/test/package/upload_test.dart +++ b/app/test/package/upload_test.dart @@ -271,7 +271,7 @@ void main() { testWithProfile( 'service account cannot upload because email not matching', fn: () async { - await withFakeAuthHttpPubApiClient( + await withFakeAuthRetryPubApiClient( email: adminAtPubDevEmail, (client) async { await client.setAutomatedPublishing( @@ -303,7 +303,7 @@ void main() { testWithProfile( 'service account cannot upload because id lock prevents it', fn: () async { - await withFakeAuthHttpPubApiClient( + await withFakeAuthRetryPubApiClient( email: adminAtPubDevEmail, (client) async { await client.setAutomatedPublishing( @@ -344,7 +344,7 @@ void main() { }); testWithProfile('successful upload with service account', fn: () async { - await withFakeAuthHttpPubApiClient( + await withFakeAuthRetryPubApiClient( email: adminAtPubDevEmail, (client) async { await client.setAutomatedPublishing( @@ -414,7 +414,7 @@ void main() { testWithProfile( 'GitHub Actions cannot upload because repository not matching', fn: () async { - await withFakeAuthHttpPubApiClient( + await withFakeAuthRetryPubApiClient( email: adminAtPubDevEmail, (client) async { await client.setAutomatedPublishing( @@ -449,7 +449,7 @@ void main() { testWithProfile( 'GitHub Actions cannot upload because ref type not matching', fn: () async { - await withFakeAuthHttpPubApiClient( + await withFakeAuthRetryPubApiClient( email: adminAtPubDevEmail, (client) async { await client.setAutomatedPublishing( @@ -484,7 +484,7 @@ void main() { testWithProfile( 'GitHub Actions cannot upload because version pattern not matching', fn: () async { - await withFakeAuthHttpPubApiClient( + await withFakeAuthRetryPubApiClient( email: adminAtPubDevEmail, (client) async { await client.setAutomatedPublishing( @@ -520,7 +520,7 @@ void main() { testWithProfile( 'GitHub Actions cannot upload because workflow_dispatch is not enabled', fn: () async { - await withFakeAuthHttpPubApiClient( + await withFakeAuthRetryPubApiClient( email: adminAtPubDevEmail, (client) async { await client.setAutomatedPublishing( @@ -555,7 +555,7 @@ void main() { testWithProfile( 'GitHub Actions cannot upload because event is not allowed', fn: () async { - await withFakeAuthHttpPubApiClient( + await withFakeAuthRetryPubApiClient( email: adminAtPubDevEmail, (client) async { await client.setAutomatedPublishing( @@ -591,7 +591,7 @@ void main() { 'GitHub Actions cannot upload because id lock prevents it', fn: () async { Future setupPublishingAndLock() async { - await withFakeAuthHttpPubApiClient( + await withFakeAuthRetryPubApiClient( email: adminAtPubDevEmail, (client) async { await client.setAutomatedPublishing( @@ -657,7 +657,7 @@ void main() { testWithProfile( 'successful upload with GitHub Actions (push, without environment)', fn: () async { - await withFakeAuthHttpPubApiClient( + await withFakeAuthRetryPubApiClient( email: adminAtPubDevEmail, (client) async { await client.setAutomatedPublishing( @@ -686,7 +686,7 @@ void main() { testWithProfile( 'successful upload with GitHub Actions (workflow_dispatch, without environment)', fn: () async { - await withFakeAuthHttpPubApiClient( + await withFakeAuthRetryPubApiClient( email: adminAtPubDevEmail, (client) async { await client.setAutomatedPublishing( @@ -724,7 +724,7 @@ void main() { ], defaultUser: 'admin@pub.dev', ), fn: () async { - await withFakeAuthHttpPubApiClient( + await withFakeAuthRetryPubApiClient( email: adminAtPubDevEmail, (client) async { await client.setAutomatedPublishing( @@ -797,7 +797,7 @@ void main() { testWithProfile( 'GitHub Actions cannot upload because environment is missing', fn: () async { - await withFakeAuthHttpPubApiClient( + await withFakeAuthRetryPubApiClient( email: adminAtPubDevEmail, (client) async { await client.setAutomatedPublishing( @@ -835,7 +835,7 @@ void main() { testWithProfile( 'GitHub Actions cannot upload because environment not matching', fn: () async { - await withFakeAuthHttpPubApiClient( + await withFakeAuthRetryPubApiClient( email: adminAtPubDevEmail, (client) async { await client.setAutomatedPublishing( @@ -874,7 +874,7 @@ void main() { testWithProfile( 'successful upload with GitHub Actions (with environment)', fn: () async { - await withFakeAuthHttpPubApiClient( + await withFakeAuthRetryPubApiClient( email: adminAtPubDevEmail, (client) async { await client.setAutomatedPublishing(